zoukankan      html  css  js  c++  java
  • 图书管理:双重哈希hash + 数组模拟邻接表、双重哈希hash +multimap,BKDRhash+multimap

    哈希表

    一、概述

          哈希算法是通过哈希函数,将一种数据转化为能够用变量或数组下标表示的数,通过哈希函数转化得到的值,称之为哈希值。哈希表的查找时间几乎是常数时间,哈希函数是决定哈希表查找效率的关键,本次就讲解其中之一的除余法。

    二、例题

          通过图书管理这道题,让我们开始学习这个算法。

    题目描述

    图书管理是一件十分繁杂的工作,在一个图书馆中每天都会有许多新书加入。为了更方便的管理图书(以便于帮助想要借书的客人快速查找他们是否有他们所需要的书),我们需要设计一个图书查找系统。

    该系统需要支持 2 种操作:

    1. add(s) 表示新加入一本书名为 s 的图书。
    2. find(s) 表示查询是否存在一本书名为 s 的图书。

    输入格式

    第一行包括一个正整数 ,表示操作数。 以下  行,每行给出 2 种操作中的某一个指令条,指令格式为:

    add s
    find s
    

    在书名 s 与指令(addfind)之间有一个隔开,我们保证所有书名的长度都不超过 。可以假设读入数据是准确无误的。

    输出格式

    对于每个 find(s) 指令,我们必须对应的输出一行 yes 或 no,表示当前所查询的书是否存在于图书馆内。

    注意:一开始时图书馆内是没有一本图书的。并且,对于相同字母不同大小写的书名,我们认为它们是不同的。

    样例

    样例输入

    4
    add Inside C#
    find Effective Java
    add Effective Java
    find Effective Java
    

    样例输出

    no
    yes

     三、题目解析

          题目大意就是将一些字符串存储起来,后期查找一下某个字符串存不存,这需要极高的查找效率,非常适合使用哈希表。

          将字符串映射成一个数存起来,查找这个字符串存不存的时候,就是查找这个数存不存在,某些不同的字符串会映射成一个数字,可以将这些数存起来。

    题目地址:https://loj.ac/problem/10034

    方法一:双重哈希hash + 数组模拟邻接表

    可以将代码的核心部分记下来,以后直接用便可

    #include<bits/stdc++.h>
    
    using namespace std;
    const int mod1 = 1e+6,mod2 = 1e+9,p1 = 47,p2 = 79,N=30000;
    int tot = 0,nxt[N+5],poi[mod1+5],end1[N+5];
    
    void insert1(int x,int y){///数组模拟邻接表
        nxt[++tot] = poi[x];///目的是排除哈希值冲突问题
        poi[x] = tot;
        end1[tot] = y;
    }
    int query(int x,int y)///查询操作
    {
        for(int i = poi[x];i;i=nxt[i])
            if(end1[i]==y)
                return 1;
        return 0;
    }
    int main(){
        int n;
        char op[10],s[205];
        cin>>n;
        while(n--){
            cin>>op;
            gets(s);
            int len=strlen(s),sum1 = 0,sum2 = 0;
            for(int i=0;i<len;i++){    ///双重哈希 核心部分
                sum1 = (sum1*p1+s[i])%mod1;/// %mod1可以缩小这个数的映射范围
                sum2 = (sum2*p2+s[i])%mod2;
            }
            if(op[0]=='a')
                insert1(sum1,sum2);
            else
                if(query(sum1,sum2))
                    printf("yes
    ");
                else
                    printf("no
    ");
        }
    }
    

    大家可能对数组模拟邻接表那里表示不解,改天单独出一个教程。

    方法二:双重哈希hash +multimap

    使用邻接表存储冲突数据的时候,我就想使用multimap是否会使代码更简洁,multimap是一个键可以有多个值,正好适用于这里。

    multmap的具体操作请参考:https://blog.csdn.net/xy_cpp/article/details/80426852?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2

    #include<bits/stdc++.h>
    
    using namespace std;
    const int mod1 = 1e+6,mod2 = 1e+9,p1 = 47,p2 = 79,N=30000;
    multimap<int,int> sign;
    int main(){
        int n;
        char op[10],s[205];
        cin>>n;
        while(n--){
            cin>>op;
            gets(s);
            int len=strlen(s),sum1 = 0,sum2 = 0;
            for(int i=0;i<len;i++){          ///双重哈希  核心部分
                sum1 = (sum1*p1+s[i])%mod1;
                sum2 = (sum2*p2+s[i])%mod2;
            }
            if(op[0]=='a')
                sign.insert(make_pair(sum1,sum2));
            else{
                int flag = 0;
                multimap<int,int>::iterator it;
                it = sign.find(sum1);                    ///找到键值sum1第一次出现的位置
                for(int i=0;i<sign.count(sum1);i++,it++) ///挨个判断value是否符合,
                    if(it->second == sum2){
                        printf("yes
    ");
                        flag = 1;
                        break;
                    }
                if(flag==0)
                    printf("no
    ");
    
            }
        }
        return 0;
    }
    

    方法三:BKDRhash+multimap

    这也是一种常用的方法

    #include<bits/stdc++.h>
    
    using namespace std;
    const int mod1 = 1e+6,mod2 = 1e+9,p1 = 47,p2 = 79,N=30000;
    multimap<int,string> sign;
    
    unsigned int BKDRhash(string s){///求哈希值
        unsigned int seed = 31,key = 0,i=0;
        while(i!=s.length())
            key = key * seed + (s[i++]);
        return key & 0x7fffffff;
    }
    
    int main(){
        int n;
        string op,s;
        cin>>n;
        while(n--){
            cin>>op;
            getline(cin,s);///输入带空格的字符串
            int key = BKDRhash(s)%N;   ///控制哈希值映射范围
            if(op[0]=='a')
                sign.insert(make_pair(key,s));
            else{
                int flag = 0;
                multimap<int,string>::iterator it;
                it = sign.find(key);
                for(int i=0;i<sign.count(key);i++,it++)
                    if(it->second == s){
                        printf("yes
    ");
                        flag = 1;
                        break;
                    }
                if(flag==0)
                    printf("no
    ");
    
            }
        }
        return 0;
    }
    

    学习是一件挺困难的事情,但绝对不是痛苦的。

  • 相关阅读:
    Python网络爬虫规则之实例
    Syncthing:同步window和linux文件
    阿里云AIoT云端一体:迎接云原生+低代码时代的到来
    低代码时代的物联网快速构建工具-YFIOs
    阿里云、华为云和腾讯云等多家物联网平台的异同
    全志 Fex文件
    le16_to_cpu
    无线充电技术简介
    AK47所向披靡,内存泄漏一网打尽
    zRAM内存压缩技术原理与应用
  • 原文地址:https://www.cnblogs.com/codepeanut/p/12920493.html
Copyright © 2011-2022 走看看