1 哈希表的定义
哈希表(hashtable),⼜称散列表,是根据关键字直接进⾏访问的数据结构。
哈希表建⽴了⼀种关键字和存储地址之间的直接映射关系,使每个关键字与结构中的唯⼀存储位置相对应。理想情况下,在散列表中进⾏查找的时间复杂度为 ,即与表中的元素数量⽆关。因此哈
希表是⼀种存储和查找⾮常快的结构。
1.2 哈希函数
将关键字映射成对应的地址的函数就是哈希函数,也叫作散列函数,记为 Hash(key) = Addr 。
哈希函数的本质也是⼀个函数,它的作⽤是,你给它⼀个关键字,它给你⼀个该关键字对应的存储位置。
1.3 哈希冲突
哈希函数可能会把两个或两个以上的不同关键字映射到同⼀地址,这种情况称为哈希冲突,也称散列冲突。起冲突的不同关键字,称它们为同义词。
2.处理哈希冲突
2.1 线性探测法
从发⽣冲突的位置开始,依次线性向后探测,直到寻找到下⼀个没有存储数据的位置为⽌,如果⾛到哈希表尾,则回绕到哈希表头的位置。
2.2 链地址法
链地址法中所有的数据不再直接存储在哈希表中,哈希表中存储⼀个指针,没有数据映射这个位置
时,这个指针为空,有多个数据映射到这个位置时,我们把这些冲突的数据链接成⼀个链表,挂在哈希表这个位置下⾯
3. 哈希表的模拟实现
3.1线性探测法
(1)创建
#include<iostream>
#include<cstring>
using namespace std;
const int N = 23;//创建一个模数
const int INF = 0x3f3f3f3f;
int h[N];
void init()
{
memset(h, INF, sizeof h);
}
int main()
{
init();
return 0;
}
(2) 哈希函数以及处理哈希冲突
除留余数法: hash(key) = key % N
但是要注意, key 有可能是负数,取模之后会变成负数。
• 负数补正的操作为:加上模数即可。
• 但是正数加上模数会变⼤,所以统⼀再取⼀次模。
最终就是 (key % N + N) % N
int f(int x)
{
int idx=(x % N + N) % N;
// 处理冲突
while (h[idx] != INF && h[idx] != x)
{
idx++;
if (idx == N)
{
idx = 0;
}
// 如果⾛到头了,就拐个弯
}
return idx;
}
(3)添加元素
void insert(int x)
{
int id = f(x);
h[id] = x;
}
(4)查找元素
bool find(int x)
{
int id = f(x);
return h[id] == x;
}
3.2链地址法
(1)创建
#include<iostream>
using namespace std;
const int N = 23;
int h[N];
int e[N], ne[N], id;
(2) 哈希函数
int f(int x)
{
return (x % N + N) % N;
}
(3)查找函数
bool find()
{
int idx = h[x];
for (i = h[idx]; i; i = ne[i])
{
if (e[i] == x;)
{
return true;
}
}
return false;
}
(4)添加元素以及处理哈希冲突
- 先判断是否已经在哈希表中;
- 如果不在,就头插在哈希值所对应的链表后。
void insert(int x)
{
if (find(x))
{
return;
}
int idx = f(x);
id++;
e[i] = x;
ne[id] = h[idx];
h[idx] = id;
}