散列(hash)-----基础版

散列可以表示为一句话:将元素通过一个函数转换为整数,使得该整数可以尽量唯一地代表这个元素!

来看一个简单的问题:给出N个正整数,再给出M个正整数,问M个数中的每个数分别是否在N个数中出现过,其中N,M<=105,且所有正整数均不超过105
例子:N=5,M=3,N个正整数为{8,2,4,7,1},M个正整数为{7,4,9},于是后者中7、4在N个正整数中出现过,而9是没有出现过。

较为简单的思路:遍历所有N个正整数,看是否一个数与所要查询的正整数x相等。这种方法的时间复杂度为O(NM),当N和M都很大的时候(105),显然无法承受的!

因此想到用空间换时间。设定一个bool数组hashTable[100010],其中hashTable[x] == true表示正整数x在N个正整数中出现过,否则,没有出现过。如此,我们就可以在一开始读入N个正整数时就可以进行预处理,即当读入的数为x时,就令hashTable[x] == true(hashTable数组初始化为false)。这种方法的时间复杂度为O(N+M)!
代码:

#include <cstdio>
const int maxn = 100010;
bool hashTable[maxn] = {false};

int main() {
    int n,m,x;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%d",&x);
        hashTable[x] = true;
    }
    for(int i=0;i<m;i++){
        scanf("%d",&x);
        if(hashTable[x] == true)
            printf("YES
");
        else
            printf("NO
");
    }
    return 0;
}

如果题目需要M个待查询的数中每个数在N个数中出现的次数,那么就可以将hashTable换成int型,然后在输入N个正整数时进行处理,即每当输入x时,就令hashTable[x]++,这种方法只需要O(N+M)的时间复杂度输出每个预查询的数出现的次数。
上面两个问题的特点:直接把输入的数作为数组的下标来对这个数组的性质进行统计!(此方法非常实用,多练习掌握)

有关散列函数内容以及解决冲突的办法,都在数据结构学科内,可以找本数据结构书籍进行学习!

字符串hash问题

字符串hansh是将一个字符串S映射为一个整数,使得该整数可以尽可能唯一地代表字符串S!

如果字符串中出现了大写、小写字母,可以把A~Z作为0~25,把a~z作为26~51,这样就变成了五十二进制转换为十进制的问题。由进制转换的结论可知,在进制转换的过程中,得到的十进制肯定是唯一的,因此就可以实现将字符串映射为整数的需求(注意:转换的整数最大为26len-1,其中len为字符串的长度)。
代码:

int hashFunc(char S[],int len){
    int id = 0;
    for(int i=0;i<len;i++){
        if(S[i] >= 'A' && S[i] <='Z')
            id = id*52+(S[i] - 'A');
        else if(S[i] >= 'a' && s[i] <= 'z')
            id = id*52+(S[i] - 'a' + 26);
    }
    return id;
}

但有时候字符串中间会出现数字,而对于数字,有以下两种处理方式:

1.和上面的小写字母的处理方法一样,在进制数后面再添加十位就可以,这里就是增大进制数至62;
2.有的题目可能是直接确定在字符串后面是确定个数的数字,那么此时的处理方法可以是前面的英文字母按照上述思路转换为整数,而末尾的数字直接拼接上去就好。举个例子:对于由三个字符加上末尾是一位数字组成的字符串“BCD4”来讲,可以先将前面的“BCD”转换为整数731,然后直接将数字4拼接上去成为7314即可。
代码:

int hashFunc(char S[],int len){
    int id = 0;
    for(int i=0;i<len;i++){
        if(S[i] >= 'A' && S[i] <='Z')
            id = id*52+(S[i] - 'A');
        else if(S[i] >= 'a' && s[i] <= 'z')
            id = id*52+(S[i] - 'a' + 26);
        else
            id = id*10+(S[i] - '0');
    }
    return id;
}

习题练习:
给出N个字符串(由恰好三个大写字母拼成),再给出M个查询字符串,问每个查询字符串在N个字符串中出现的次数。
代码:

#include <cstdio>
const int maxn = 100;
char S[maxn][5],temp[5];
int hashTable[26*26*26+10];

int hashFunc(char S[],int len){
    int id = 0;
    for(int i=0;i<len;i++){
        if(S[i] >= 'A' && S[i] <='Z')
            id = id*52+(S[i] - 'A');
    }
    return id;
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        sacnf("%d",&S[i]);
        int id = hashFunc(S[i],3);
        hashTable[id]++;
    }
    for(int i=0;i<n;i++){
        scanf("%s",temp);
        int id = hashFunc(temp,3);
        printf("%d
",hashTable[i]):
    }
    return 0;
}

hash进阶版:https://www.cnblogs.com/techgy/p/15039772.html

原文地址:https://www.cnblogs.com/techgy/p/15037113.html