散列表
散列表是普通数组的推广。
设计散列表主要是对哈希函数和处理冲突碰撞的选择和设计,好的哈希函数h可以使关键字比较均匀的散列在哈希表中,冲突较少。所谓“好”的哈希函数的主导思想,是使h尽可能地随机,减少碰撞,但是不可能完全避免碰撞,因为关键字域的势 |U|>m,m为散列表的槽数,总会有两个不同的关键字映射到同一个槽中,产生碰撞。
1、哈希函数
一个好的哈希函数应(近似地)满足简单一致散列的假设:每个关键字都等可能的散列到m个槽位的任何一个中去,并与其他的关键字已被散列到哪个槽位中无关。
不幸的是,一般情况下不太可能检查这一条件是否成立,因为人们很少能知道关键字所符合的概率分布,而各关键字可能并不是完全相互独立的。
算法导论中列举了四种哈希函数:直接定址法、除法散列法、乘法散列法和全域散列法。
其中除法散列法和乘法散列法利用了启发式的方法,第三个利用了随机化的技术。
a、除法散列法(模除法)
h(k)=k mod m
m的取值常常是与2的整数幂不太接近的质数(m是指散列表的槽数)。
b、乘法散列法
h(k)=|m*(k*A mod 1)| (取底)
用关键字k乘以常数A(0<A<1),并取出kA的小数部分。然后用m乘以这个值,对结果取底。
对 m 的选择没有特别的要求,一般选择为 2 的整数次幂
Knuth认为 A=(sqrt(5)-1)=0.6180339887......,取这个值比较理想
c、全域散列
h(k)=((a*k+b) mod p) mod m (哈希函数族)
选择一个足够大的质数p,使得每一个可能的关键字k都落到0到p-1之间(包括0和p-1)。
Zp表示集合{0,1,……,p-1},Zp*表示集合{1,2,……,p-1}。
容易知道p>m(p>=|U|>m)
a属于Zp*,B属于Zp
从函数族中随机的选取一个作为哈希函数
d、在严蔚敏的数据结构中还介绍了其他的构造哈希函数的方法(比如平方取中法,折叠法等)
2、处理碰撞的方法
a、哈希链接法
把冲突的关键字存储在冲突槽的链表中
在简单一致散列的假设下,一次不成功查找的期望时间为O(1+α),平均情况下一次成功的查找也需要同样的时间
b、开放寻址法
线性探测 h(k,i)=(h'(k)+i) mod m,i=0,1,……,m-1
二次探测 h(k,i)=(h'(k)+c1*i+c2*i*i) mod m 严蔚敏的数据结构中 c1=0,c2=-1
双重哈希(再哈希法) h(k,i)=(h1(k)+i*h2(k)) mod m
每一个关键字的探查序列
<h(k,0),h(k,1),……,h(k,m-1)>
直到探寻到合适的位置为止
c、完全散列法
基本思想比较简单,采用两级的散列方案,每一级都采用全域散列。
有点类似哈希链接法,只不过,在冲突槽的链表上再采用一次全域哈希。
d、严蔚敏的数据结构中还介绍了 建立一个公共溢出区 的方法
------------------------------------一个小例子(哈希链接法 处理冲突)---------------------------------------
代码
1 /*
2 * 算法导论 第十一章 散列表
3 *
4 * 哈希函数:模除散列法
5 * 冲突避免:哈希链接法
6 * */
7 /* 输出:14 1
8 1 0
9 */
10 #include<stdio.h>
11 #include<stdlib.h>
12 struct hash_node{
13 struct hash_node *next;
14 int data;
15 };
16 struct hash_table{
17 struct hash_node *base;
18 unsigned int count;
19 unsigned int size;
20 };
21 unsigned int hash1(int key, int m)
22 {
23 /*int rt=-1;*/
24 return key%m;
25 }
26 unsigned int hash2(int key, int m)
27 {
28 return 1+(key%(m-1));
29 }
30 unsigned int hash(int key, int m, int i)
31 {
32 return (hash1(key,m)+i*hash2(key,m))%m;
33 }
34 void chain_insert(struct hash_table *tbl, int key)
35 {
36 unsigned int pos=-1;
37 struct hash_node *new_node=NULL;
38 new_node=(struct hash_node *)malloc(sizeof(struct hash_node));
39 new_node->data=key;
40 new_node->next=NULL;
41
42 pos=hash(key,tbl->size,0);
43
44 if(tbl[pos].count==0)
45 {
46 tbl[pos].base=new_node;
47 tbl[pos].count += 1;
48 }
49 else
50 {
51 new_node->next=tbl[pos].base;
52 tbl[pos].base=new_node;
53 tbl[pos].count += 1;
54 }
55 }
56 void chain_search(struct hash_table *tbl, int key, unsigned int *row,unsigned int *col)
57 {
58 int i=0;
59 int idx=-1;
60 struct hash_table tb;
61
62 idx=hash(key,tbl->size,0);
63
64 if(tbl[idx].count > 0)
65 {
66 tb=tbl[idx];
67 while(tb.base!=NULL && tb.base->data!=key)
68 {
69 tb.base=tb.base->next;
70 i += 1;
71 }
72
73 *row=idx;
74 *col=i;
75
76 if(tb.base==NULL)
77 {
78 *row=-1;
79 *col=-1;
80 }
81 }
82 else
83 {
84 *row=-1;
85 *col=-1;
86 }
87 }
88 #define m 13
89 int main()
90 {
91 int i=0;
92 int row, col;
93 struct hash_table tbl[m];
94
95 for(i=0;i<m;i++)
96 {
97 tbl[i].base=NULL;
98 tbl[i].count=0;
99 tbl[i].size=m;
100 }
101 chain_insert(tbl,1);
102 chain_insert(tbl,14);
103 chain_search(tbl,14,&row,&col);
104
105 printf("%d ",tbl[1].base->data);
106 printf("%d ",tbl[1].base->next->data);
107 printf("\n%d %d\n",row,col);
108 return 0;
109 }

浙公网安备 33010602011771号