散列表

散列表是普通数组的推广。

设计散列表主要是对哈希函数和处理冲突碰撞的选择和设计,好的哈希函数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 }

 

 

 

阅读全文
类别:算法 查看评论

posted on 2010-04-19 20:50  janqii  阅读(733)  评论(1)    收藏  举报

导航