hash表构建方法编程技巧
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一、开式寻址法解决冲突
• 假定键值是大于零的整数,所选用的 Hash函数是h(x),并用数组t[m]作为Hash表, 这个表共有m个位置。
• 1.建立Hash表的插入运算。
• 为了把键值k存入Hash表,先计算出h(k)=i.
• 如果t[i] 是一个空位置(即t[i]=0),那么把k存 入t[i],插入过程结束;
i=3+2=5; if (t[5]<=0 {t[5]=36;return(0); }
0
1
2
3
4
25 48
(1)
(1)
5
6
7
8
9
10
36 94
63
75 32
(3)
(1)
(1)
(1)
(1)
int search1(t, k) int t[ ],k; {int i ,j; i =h(k); for(j=0;j<m&&t[(i+j)%M]!=k&&t[(i+j)%M]!=0; j++); i=(i+j)%M; if(t[i]==k)return(i); return(-1); }
• 如果t[i]不是空位置(即t[i]≠0),那么再判断t[i] 是否等于k,
• 若t[i]=k,则键值k已在Hash表中,插入过程结 束;
• 否则,t[i]已被另一个键值所占用(发生冲 突),此时必须为k找另一个空位置,最简单 的办法是进行线性探测,我们可使用下面的 循环探测地址序列:
• (i+1) mod m • (i+2) mod m • ……
0
1
2
70
(8)
5
6
7
36 94 18
(6)
(1)
(1)
3
4
25 48
(1)
(1)
8
9
10
63
75 32
(1)
(1)
(1)
i=h(k)=10; //Hash (32) = 10 for(j=0;j<m&&t[(i+j)%M]!=k&&t[(i+j)%M]>0;j++);
i=(i+j)%M; if (t[i]<=0 {t[i]=k;return(0); } j=0;j<m=11&&t[(10+0) ]!=32&&t[(10+0)] =0;
使用散列方法进行搜索不必进行多次关键码 的比较, 搜索速度比较快, 可以直接到达或逼 近具有此关键码的表项的实际存放地址。
散列函数是一个压缩映象函数。关键码集合 比散列表地址集合大得多。因此有可能经过 散列函数的计算,把不同的关键码映射到同 一个散列地址上,这就产生了冲突。
示例:有一组表项,其关键码分别是
• 34 mod 10=4 • 就能在数组元素T[4]中找到它。 • 出现h(keyi)=h(keyj),称这种情况是冲突。 • 散列存贮的两个主要问题是:一是选取
散列函数;二是选取解决冲突的办法。
静态散列方法
散列方法在表项存储位置与其关键码之间建 立一个确定的对应函数关系Hash( ),使每个 关键码与结构中一个唯一存储位置相对应:
12361, 07251, 03309, 30976
采用的散列函数是
hash(x) = x % 73 + 13420 其中,“%”是除法取余操作。 则有:hash(12361) = hash(07250) = hash(03309) = hash(30976) = 13444。就是说, 对不同的关键码, 通 过散列函数的计算, 得到了同一散列地址。我们称这 些产生冲突的散列地址相同的不同关键码为同义词。 由于关键码集合比地址集合大得多, 冲突很难避免。 所以对于散列方法, 需要讨论以下两个问题:
k,则查找失败。
• 3.删除键值K.
• 首先在Hash表t[m]上进行查找。
• 如果查找成功,假定t[j]=k,那么应把t[j]删 除。但是,我们不能把t[j]置成空,而只 能标上已被删除的标记,否则将会影响 以后的查找。因此,在插入时,凡遇到 标有删除标记的位置都可以插入;而在 查找时,凡遇到标有删除标记的位置, 还要继续查下去。
对于给定的一个关键码集合, 选择一个计算 简单且地址分布比较均匀的散列函数, 避免 或尽量减少冲突; 拟订解决冲突的方案。
散列函数
构造散列函数时的几点要求: 散列函数应是简单的,能在较短的时间内计
算出结果。 散列函数的定义域必须包括需要存储的全部
关键码, 如果散列表允许有 m 个地址时, 其 值域必须在 0 到 m-1 之间。
• 对 的 中于唯的F一位中值置的与就任之 由一h对(结k应e点y,i)K决Ki,定i存都。放有在一数个组h(Tk[emyi])
这种存贮线性表F的方法,称为散列存贮。 称函数h(x)为散列函数(HASH函数)。
称存放结点的数组T(m)为散列表(Hash表).
设F是含有六个结点的线性表,其中 K0=(9,e) ,k1=(12,b), k2=(20,e),
把来自hash表中的一些链表叫做链,称该 方法为拉链法。
1.Advantage of chaining
The first,is that considerable space may be saved. Since the hash table is a continuous array, enough space must be set aside at compilation time to avoid overflow.
Hash (20) = 0 Hash (7) = 7
Hash (50) = 0 Hash (14) = 4
设散列表 HT[26], m = 26。采用线性探查法 处理冲突, 则散列结果如图所示。
0
1
2
3
4
20 1 11
314(1)来自(1)(2)(3)
(1)
5
6
7
8
9
50 14 7
(6)
(3)
(1)
• (i+m-1) mod m • 一旦找到一个空位置,就把k存入刚探测
到的空位置上,插入过程结束; • 如果用完整个探测地址序列还没有找到
空位置,那么Hash表满,插入失败,过 程结束。 •例
• Hash(x)=x%10
Hash (1) = 1 Hash (4) = 4
Hash (11) = 1 Hash (31) = 1
i=(10+0)%11=10; if (t[10]<=0{t[10]=32;return(0);}
0
1
2
3
4
5
6
7
8
9
10
32
(1)
i=h(k);
for(j=0;j<m&&t[(i+j)%M]!=k&&t[(i+j)%M]>0;j++);
i=(i+j)%M; if (t[i]<=0 {t[i]=k;return(0); }
The second advantage of keeping only pointers in the hash table is that it allows simple and efficient collision handing.
i=h(k);
for(j=0;j<m&& t[(i+j)%M]!=k&&t[(i+j)%M]>0;j++);
i=(i+j)%M; if (t[i]<=0 {t[i]=k;return(0); }
Hash (32) = 10 Hash (75) = 9 Hash (63) = 8 Hash (48) = 4 Hash (94) = 6 Hash (25) = 3 Hash (36) = 3 Hash (18) =7 Hash (70) = 4
i =h(k); for(j=0;j<m&&t[(i+j)%M]!=k&&t[(i+j)%M]!=0;j++); i=(i+j)%M;if(t[i]==k)return(i);
i =h(36)=3;for(j=0;j t[3+0]=25 !=(k=36)!=0;j++); j =1; t[3+1]=48 !=(k=36)!=0;j++); j =2;t[3+2]!=36 =(k=36); i=(3+2)%11=5;if(t[5]==36)return(5);
Hash (32) = 10 Hash (75) = 9 Hash (63) = 8
Hash (48) = 4 Hash (94) = 6 Hash (25) = 3 i=h(36)=3;for(j=0;j<11&&t[3+0]=25!=(k=36)//冲突j=1;
t[3+1]=48!=(k=36);j=2 &&t[3+2]=0;
• 2.查找键值k • 首先计算出h(k)=i. • 如果t[i]=k,则查找成功,查找过程结束; • 如果t[i]不等于k,那么必须按照上面所给
出的循环探测地址序列进行查找。
• 查找过程一直进行到下面三种情况之一 出现为止:
• (1)当前位置上的键值等于k,则查找 成功。
• (2)找到一个空位置,则查找失败。 • (3)用完循环探测地址序列还没有找到
K3=(26,a), k4=(34,d), k5=(48,f). 若取散列函数h(x)=x mod 10,并使用 能存放十个结点的数组T[10]作为Hash表, 则下图表示了F的散列存贮的状况。
0 20 E 1
2 12 b 3
4 34 d 5
6 26 a 7
8 48 f 99 C
• 如果我们想找到key4=34的结点K4,那么 只要计算出
Address = Hash ( Rec.key ) 在搜索时, 先对表项的关键码进行函数计算,
把函数值当做表项的存储位置, 在结构中按 此位置取表项比较。若关键码相等, 则搜索 成功。在存放表项时, 依相同函数计算存储 位置, 并按此位置存放。这种方法就是散列 方法。
在散列方法中使用的转换函数叫做散列函数。 按此方法构造出来的表或结构就叫做散列表。
Collision resolution by chaining
We can take the hash table itself as an array of pointers to the record,that is,as an array of linked lists.
It is traditional to refer to the linked lists from hash as chains and call this method collision resolution by chaining.
If the hash table contains only pointers to the records,pointers that require only one word each,the size of the hash table may be reduced by a large factor and will become small relative to the space available for the record,or for other uses.
0
1
2
3
4
70
25 48
5
6
7
36 94 18
(3)
8
9
10
63
75 32
int deletel(t,k) int t[ ],k; {int i,j; i=h(k); for(j=0;j<m &&t[(i+j)%M]!=k&&t[(i+j)%M]!=0;j++); i=(i+j)%M; if(t[i]==k) {t[i]= -1; return(0); } return(1); }
int insert1(t,k) int t[ ],k; {int i,j; i=h(k); for(j=0;j<m&& t[(i+j)%M]!=k&&t[(i+j)%M]>0;j++); i=(i+j)%M; if (t[i]<=0)
{t[i]=k; return(0); } return(1); }
散列(Hashing)存贮
• 假定键值均是正整数. • 散列存贮是通过对结点的键值做某种运
算来确定具有该键值的结点的存放位置。
• 设 而有结线点k性i的表键F=值(k为1,kK2,e…yi,,kn记-1)F和中数所组有T结[m点], 的键值的集合为S.
• h(x)是从S到整数区间[0,m-1]上的一个一 一对应函数。
• 实现上面各种运算的程序。
• 我们假定所使用的键值是大于零的整数, 用0对Hash表t[M]进行初始化,用-1作 为删除标记,所使用的Hash函数是h(x).
• #define M 100 void makenull(t) int t[ ]; {int i; for(i=0;i<m;i++)t[i]=0; }