假设有一个数组An = {81, 62, 55, 43, 97, 76, 49, 98, 64}; 如果判断任意一个数x是否在该给定的数列An中呢? 作为一个正常的人, 我们自然会想, 把x先和81, 62, ..., 逐一比较,这样复杂度是O(n)。
提出哈希表的人, 确实很了不起, 因为他没有采用正常人的思路。我们来看看哈希表人的思路:
哈希的思想是:你给我一个key和value,我用这个key和我设计的哈希函数去算出一个地址,然后把value存到这个地址。
在查询的时候,我用x和哈希函数去找到地址中的值来比对,所以复杂度是O(1)。具体见代码:
#include <iostream>
using namespace std;
int hash(int key, int n)//用key作为哈希函数的参数
{
return key % n;
}
int main()
{
int a[9] = {81, 62, 55, 43, 97, 76, 49, 98, 64};
int b[9] = {-1, -1, -1, -1, -1, -1, -1, -1, -1};
int i = 0;
for(i = 0; i < 9; i++)
{
b[hash(a[i], 10)] = a[i];//把value存到哈希函数计算得到的地址中去
}
int x = 0;
for(x = 0; x < 1000000; x++)
{
if(b[hash(x, 10)] == x)
{
cout << x << " is in a[...]" << endl;
}
}
return 0;
}
上述程序的结果为:
43 is in a[...]
49 is in a[...]
55 is in a[...]
62 is in a[...]
64 is in a[...]
76 is in a[...]
81 is in a[...]
97 is in a[...]
98 is in a[...]
上面例子中的数组是故意设计的,各位分别为1~9.为什么要选择那么特殊的数组An呢? 因为经历h(z) = z %10的哈希变化后, 不会冲突。那么假如数组中多一个元素11。那么经过哈希函数后就会有哈希冲突。
如果遇到哈希冲突怎么办
解决方法有两类,
开放寻址法(open addressing)和链表法(chaining)。
1. 开放寻址法
2. 链表法
链表法是一种更加常用的散列冲突解决办法,相比开放寻址法,它要简单很多。我们来看这个图,在散列表中,
“桶(bucket)”或者“槽(slot)”会对应一条链表,所有散列值相同的元素我们都放到相同槽位对应的链表中。
参考:
1.https://blog.csdn.net/stpeace/article/details/38615647
2.https://time.geekbang.org/column/article/64233