图书管理:双重哈希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\n");
            else
                printf("no\n");
    }
}

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

方法二:双重哈希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\n");
                    flag = 1;
                    break;
                }
            if(flag==0)
                printf("no\n");

        }
    }
    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\n");
                    flag = 1;
                    break;
                }
            if(flag==0)
                printf("no\n");

        }
    }
    return 0;
}

 

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

posted @ 2020-04-15 00:06  code花生  阅读(247)  评论(0编辑  收藏  举报