数据结构(C语言描述)散列表

1 集合的基本概念

集合是表示事物的最有效的数学工具之一。
下面讨论各种以集合为基础的抽象数据类型,并研究在计算机上实现的有效方法。

1.1 集合的定义和记号

集合是由元素(成员)组成的一个类。集合的成员可以是一个集合,也可以是一个原子。
同一个元素在一个集合中不能多次出现
有时需要表示有重复元素的集合,这时允许同一个元素在集合中多次出现。这样的集合称为多重集合
当集合中的原子具有线性序关系(或称全序关系)“<”时,称集合为有序集(全序集或线性序集)。“<”是集合的一个线性序,它有如下性质:
(1)若a,b是集合中任意两个原子,则a<b,a=b和b<a三者必居其一。
(2)若a,b和c是集合中的原子,且a<b,b<c,则a<c(传递性)。

将集合的元素称为记录,每个记录有多个(或域)来表示元素的各种属性
通过键值可以唯一地确定集合中的一个元素。
键只是元素记录中许多域中的一个。
集合中元素的列举顺序是任意的,如{1,3}和{3,1}表示同一个集合。
关于集合的最基本的运算是并、交、差运算。
设A和B是两个集合。
把他们所有的元素合并在一起组成的集合,叫做集合A与集合B的并集,记作A∪B,读作A并B。
由所有属于集合A且属于集合B的元素所组成的集合,叫做集合A与集合B的交集,记作A∩B。
则所有属于A且不属于B的元素构成的集合,叫做集合A减集合B(或集合A与集合B之差),类似地,对于集合A、B,把集合{x∣x∈A,且x∉B}叫做A与B的差集。例如:
A={1,2,3},B={2,4},则A-B={1,3}。

1.2 定义在集合上的基本运算

常用的集合运算。其中大写字母表示一个集合,小写字母表示集合中的一个元素。
(1) SetUnion(A,B) , 并集运算:其运算结果为集合A与集合B的并集。
(2) SetIntersection(A,B) , 交集运算:其运算结果为集合A与集合B的交集。
(3) SetDifference(A,B) , 差集运算:其运算结果为集合A与集合B的差集。
(4) SetAssign(A,B) , 赋值运算:将集合B的值赋给集合A。
(5) SetEqual(A,B) , 判等运算:当集合A与集合B相等时返回1, 否则返回0。
(6) SetMember(x,S) ,成员运算:其中x与集合S的元素有相同的类型。当x属于S时,返回1,否则返回0。
(7) Setlnsert(x, S) , 插入运算:将元素x插入集合S中。x与集合S中的元素具有相同类型。当x原来就是集合S中的一个元素时,不改变集合S。
(8) SetDelete(x,S) , 删除运算:将元素x从集合S中删去。若x不属于集合S,则不改变集合S。

2 简单集合的实现方法

2.1 用位向量实现集合

用位向量实现的集合结构Bitset定义如下。
在这里插入图片描述

2.1.1 函数SetInit(size)

函数SetInit(size)创建一个用位向量实现、可存储集合大小为size的空集。
在这里插入图片描述

2.1.2 函数SetAssign(A,B)

函数SetAssign(A,B)通过复制表示集合的位向量来实现赋值运算。
在这里插入图片描述

2.1.3 函数SetMember(x,S)

函数SetMember(x,S)通过检测元素在表示集合的位向量中相应的位来判定成员属性。
在这里插入图片描述
在这里插入图片描述

2.1.4 函数SetEqual(A,B)

函数SetEqual(A,B)通过检测集合A和B的位向量来判定集合A和B是否相等。
在这里插入图片描述

2.1.5 函数SetUnion(A,B)

函数SetUnion(A,B)通过集合A和B的位向量按位或来实现并集运算。
在这里插入图片描述

2.1.6 函数SetIntersection(A,B)

在这里插入图片描述

2.1.7 函数SetDifference(A,B)

函数SetDifference(A,B)通过集合A和B的位向量按位与和按位异或来实现差集运算。
在这里插入图片描述

2.1.8 函数SetInsert(x,S)

函数SetInsert(x,S)通过将集合S的位向量相应位置1来实现元素插入运算。
在这里插入图片描述

2.1.9 函数SetDelete(x,S)

函数SetDelete(x,S)通过清除集合S的位向量相应来实现元素删除运算。
在这里插入图片描述

2.2 用链表实现集合

链表可用于表示一个无穷全集合的子集。
链表可分为无序链表和有序链表两种类型。
在求两个大小为n的集合的交时,用无序链表表示这两个集合,需要O(n^ 2)次。而用用序链表表示这两个集合,需要O(n)次。

用有序链表可实现集合Set如下。
在这里插入图片描述
有序链表的结点类型node如下。
在这里插入图片描述

2.2.1 函数SetInit()

函数SetInit()创建一个空集合。
在这里插入图片描述

2.2.2 函数SetEmpty(S)

函数SetEmpty(S)判定集合S是否为空集合。
在这里插入图片描述

2.2.3 函数SetSize(S)

函数SetSize(S)返回集合S的大小。
在这里插入图片描述

2.2.4 函数SetAssign(A,B)

函数SetAssign(A,B)通过复制表示集合的链表来实现赋值运算。
在这里插入图片描述

2.2.5 函数SetInter(A,B)

函数SetInter(A,B)通过扫描集合A和B的链表来实现交集运算。
在这里插入图片描述在这里插入图片描述

2.2.6 函数SetInsert(x,S)

函数SetInsert(x,S)通过向表示集合S的链表插入元素x来实现元素插入运算。
在这里插入图片描述

3 散列技术

在算法设计中,经常要判定某个元素是否在给定的集合中,并且要不断地对这个集合进行元素的插入和删除操作。
以集合为基础,并支持SetMember,SetInsert和SetDelete三种运算的抽象数据类型有一个专门的名称,叫做符号表。

3.1 符号表

可以用表示集合的链表位向量来实现符号表。另一种简单方法是用一个定长数组来存储集合中的元素。这个数组带有一个游标last,指示集合的最后一个元素在数组中的存储位置。
用数组实现符号表的结构定义如下。
在这里插入图片描述
在这里插入图片描述

3.1.1 函数TableInit(size)

TableInit(size)创建一个定长数组大小为size的空符号表。
在这里插入图片描述

3.1.2 函数TableMember(x,T)

符号表的成员查询函数TableMember(x,T)实现如下。
在这里插入图片描述

3.1.3 函数TableInsert(x,T)

符号表的元素插入运算TableInsert(x,T)实现如下。

在这里插入图片描述

3.1.4 函数TableDelete(x,T)

符号表的元素删除运算TableDelete(x,T)实现如下。
在这里插入图片描述
小结:
用数组实现含有n个元素的符号表,在最坏情况下运算TableMember、TableInsert和TableDelete所需的计算时间为O(n)

散列有两种形式。一种是开散列(外部散列),它将符号表元素存放在一个潜无穷的空间里,能处理任意大小的集合。
另一种是闭散列(内部散列),它使用一个固定大小的存储空间,所能处理的集合大小不能超过其存储空间大小。

3.2 开散列

开散列的基本思想是将集合的元素(可能有无穷多个)切分成有限个类,例如,划分为0,1,…B-1这个类。
用散列函数将集合中的每个元素x映射到0,1,B-1之一,h(x)的值就是x所属的类
函数h(x)的值称为元素x的散列值。上而所说的每一个类称为一个桶,并且称x属于桶h(x)。
开散列表是将数组和表结合在一起的一种数据结构。
将集合中的元素均匀地散列到各个桶中是散列技术的一个关键。

在字符串集合上定义的散列函数hash1(x)。
在这里插入图片描述

用开散列表实现的字符表结构OpenHashTable定义如下。
在这里插入图片描述
其中,ht是桶数组,size是桶数组的大小;hf(x)是元素x的散列函数。

3.2.1 函数HTInit(nbuckets,hashf(x))

在这里插入图片描述
在这里插入图片描述

函数HTInit(nbuckets,hashf(x))创建一个空的开散列表,其桶数组的大小为nbuckets,散列函数为hashf(x)。

3.2.2 函数HTMember(x,H)

在这里插入图片描述

开散列表OpenHashTable的成员查询函数HTMember(x,H)根据元素x的散列函数值确定存储该元素的桶号,然后调用相应的表定位函数返回查询结果。

3.2.3 函数HTInsert(x,H)

在这里插入图片描述

开散列表OpenHashTable的成员插入运算HTInsert(x,H)根据元素x的散列函数 值确定存储该元素的桶号,然后在该桶的表首插入元素x。

3.2.4 函数HTDleete(x,H)

在这里插入图片描述

开散列表OpenHashTable的成员删除运算HTDleete(x,H)根据元素x的散列函数值确定存储该元素的桶号,再调用相应的表元素删除函数删除元素x。

3.3 闭散列

闭散列表将符号表的元素直接存放在桶数组单元中,而不用桶数组来存放链表。因此闭散列表中的每个桶都只能存放集合的一个元素。
用闭散列表实现的符号表结构HashTable定义如下。
在这里插入图片描述
其中, ht是桶数组; size是桶数组的大小; 数组state用于表示桶单元的占用情况。
当state[k] 的值为0时, 表示桶单元h[闪已被占用;
当state[k] 的值为1时, 表示桶单元ht[k] 为空桶;
当state[k] 的值为2时, 表示桶单元h[k] 曾被占用, 但其中元素已被删除。
hf(x) 是元素x的散列函数。

3.3.1 函数HTInit(divisor,hashf(x))

函数HTInit(divisor,hashf(x))初始化桶数组ht和state,将每个桶都都设置为空桶,创建一个空散列表。
在这里插入图片描述
在这里插入图片描述

3.3.2 函数FindMatch(x,H)

函数FindMatch(x,H)在散列表H的桶数组中查找元素x,并返回它在桶数组中的位置。当x不在桶中,函数的返回值为H->size。
在这里插入图片描述

3.3.3 函数HashProb(i)

解决地址冲突的探测函数HashProb(i)是线性探测函数。
在这里插入图片描述

3.3.4 函数Unoccupied(x,H)

函数Unoccupied(x,H)返回散列表H的桶数组中可存储元素x的未占用桶单元位置k,即桶单元ht[k]是空桶或桶单元ht[k]曾被占用,但其中元素已被删除。当找不到未占用桶单元时,函数的返回值为H->size,表明桶数已满。
在这里插入图片描述

3.3.5 HTMember(x,H)

闭散列表HashTable通过函数FindMatch实现成员查询函数HTMember(x,H)。
在这里插入图片描述

3.3.6 函数HTInsert(x,H)

函数HTInsert(x,H)先调用函数HTMember,确定元素x不在散列表H中后,再用函数Unoccupied计算出元素x在桶数组中的可插入位置,并在此位置插入元素x。
在这里插入图片描述

3.3.7 函数HTDelete(x,H)

函数HTDelete(x,H)在FindMatch找到元素x所在的桶数组单元i后,将该单元所对应的状态state[i]的位置的2,表明ht[i]中元素x已被删除。
在这里插入图片描述

3.4 散列函数及其效率

下面介绍几种计算简单且效果较好的散列函数构造方法。

3.4.1 除余法

选择一个适当的正整数m,用m去除键值,取所得的余数作为散列函数值,即h(k)=k%m。

3.4.2 数乘法

用数乘法构造散列函数是先选择一个纯小数a,0<a<1,然后对于键值k和散列表的桶数B,构造相应的散列函数值:
在这里插入图片描述
数乘法的一个优点是构造出散列函数值在散列表中分布的均匀不依赖于桶数B。

3.4.3 平方取中法

平方取中法是较随机的一种散列方法。

3.4.4 基数转换法

基数转换法是将键值看成用另一个进制表示的数后,再将它转换为原来进制表示的数,取其中若干位作为散列函数值。一般取大于原来基数的数作为转换的基数,并且这两个基数是互素的。

3.4.5 随机数法

选择一个随机函数作为散列函数,取键的随机函数值作为它的散列函数值,即h(k)=random(k)。其中,random为随机函数。通常,当键的长度不等时,采用随机数法结构散列函数效果较好。

3.5 闭散列的重新散列技术

采用线性重新散列技术不可避免地会出现散列表中元素的“聚集”现象,即散列表中成块的连续地址被占用。为了减少聚集的机会,应该采用跳跃式的重新散列技术。

3.5.1 二次散列技术

二次重新散列技术选取的探查桶序列为
在这里插入图片描述
其中
在这里插入图片描述
虽然二次重新散列减少了元素聚集的可能性,但用此方法不易探查到整个散列表。只有当B为形如4j+3的素数时,才能探查到整个散列表。

3.5.2 随机重新散列技术

在这里插入图片描述

3.5.3 双重散列技术

在这里插入图片描述
在这里插入图片描述

总结

散列表及其实现方法。
散列表是常用的实现字符表的方法。
散列函数构造方法有除余法、数乘法、平方取中法、基数转换法和随机数法。
闭散列表的重新散列技术有线性重新散列技术、二次重新散列技术、随机重新散列技术和双重散列技术。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值