散列是一种常用的的查找方法,首先来看一个简单地问题。
问题1:整数查找
给定N个正整数,在给定M个正整数,问着M个数中的每一个是否在之前的N个数中出现过。应对这个问题最简单的思想就是逐个暴力遍历匹配,然而这种方法的时间复杂度过高,为O(NM)
。
那是否有更快的方法?——我们想到了空间换时间。具体怎么做呢:首先创建一个bool型数组hashTable[100010]={false}
,然后输入N个数,将每个数x对应的hashTable[x]
设置为true
。当需要查找数y是否在N个数中出现过,只需要判断是否hashTable[y]==true
即可。这样做的时间复杂度为O(N+M)
。
代码实现:
#include <cstdio>
#include<cmath>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX 100010
bool hashTable[MAX]={false};
int Table[MAX]={0};
int main()
{
int n,m,k;
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
{ scanf("%d",&Table[i]);
hashTable[Table[i]]=true;}
for(int i=0;i<m;i++)
{ scanf("%d",&k);
if (hashTable[k]==true) printf("YES\n");
else printf("NO\n");}
}
思维扩展:如果想查询M中的数在N中出现的次数,那么只需要吧hashTable
改为int
型即可。
问题2:字符串哈希
在问题1中,我们直接将输入的数作为数组的下标来对这个数的性质进行统计 ,这是一种很好的空间换时间策略。然而,当输入为非常大的数或者负数甚至是字符串,有没有解决这种情况的方法呢?
答案就是散列(Hash)。散列方法可以总结称一句话,“将一个元素通过一个函数映射为一个整数,使这个整数近可能的唯一代表这个元素”,这个转换的函数便是散列函数。元素为整数的散列函数有很多种构造方法,就不一一列举了。我们重点研究元素为字符串时的哈希方法:
假设字符串仅有A~Z的字符构成,不妨将其视为0 to 25,依照二十六进制转化为十进制的思路,通过 以下代码实现进制转换,从而将字符串映射为整数:
int hashFunc(char S[],int len)
{
int Y=0;
for (int i=0;i<len;i++)
Y=Y*26+S[i]-'A';
return Y;
}