今天我们来看看数据结构运算之查找的最后一种——散列表查找(也叫哈希表查找,主要是利用hash函数)。
1.散列表的概念:

注意:散列函数计算得到的散列值是一个非负整数;


2.散列表的构造方法:(方法有很多种,这里就只是列出了比较常用的两种方法)


注意质数这一点。p 应为小于等于m,最好是接近m 的最小质数
3.解决冲突的几种方法:
其中线性探测法:


取余13,就有13种可能,就可以有13个单链表表头。

4.散列表的查找:

假设采用开放地址法处理冲突,查找过程为:
对于给定的key,计算hash 地址index = f(key)
如果数组 arr[index] 的值为空,则查找不成功
如果数组 arr[index] == key,则查找成功
否则,使用冲突解决方法求下一个地址,直到
arr[index] == key or arr[index] == null

可以看到使用两种解决冲突的方法的平均查找长度是不同的 
最后,说几点关于散列表的结论:

当数据量比较小、装载因子小的时候,适合采用开放寻址法
基于链表的散列冲突处理方法比较适合存储大对象、大数据量的散列表,而且,比起开放寻址法,它更加灵活,支持更多的优化策略,比如用红黑树代替链表。
文章最后分享以下散列表的查找算法实现:
1. 定义一个散列表的结构以及一些相关常数
#define SUCCESS 1
#define UNSUCCESS 0
#define HASHSIZE 12 // 定义散列表长为数组的长度
#define NULLKEY -32768
typedef struct{
int *elem; // 数据元素存储基址,动态分配数组
int count; // 当前数据元素个数
}HashTable;
int m = 0; // 散列表表长,全局变量
注意:HashTable:散列表结构 elem:动态数组
2. 对散列表进行初始化
/* 初始化散列表 */
Status InitHashTable(HashTable *H){
int i;
m = HASHSIZE;
H->count = m;
H->elem = (int *)malloc(m * sizeof(int));
for(i = 0; i < m; i++)
H->elem[i] = NULLKEY;
return OK;
}
3.定义散列函数,可根据不同情况更改算法
/* 散列函数 */
int Hash(int key)
{ return key % m; //除留余数法 }
4. 对散列表进行插入操作
/* 插入关键字进行散列表 */
void InsertHash(HashTable *H, int key){
int addr = Hash(key) // 求散列地址
while(H->elem[addr] != NULLKEY) // 如果不为空,则冲突
addr = (addr + 1) % m; // 开放定址法的线性探索
H->elem[addr] = key; // 知道有空位后插入关键字
}
5. 通过散列表查找关键字
/* 散列表查找关键字 */
Status SearchHash(HashTable H, int key, int *addr){
*addr = Hash(key); // 求散列地址
while(H.elem[*addr] != key){// 如果不为空,则冲突
*addr = (*addr + 1) % m; // 开方定址法的线性探索
if(H.elem[*addr] == NULLKEY || *addr = Hash(key)){
/* 如果循环回到原点 */
return UNSUCCESS; // 则说明关键字不存在
}
}
return SUCCESS;
}
好啦,关于散列表(哈希表)的内容就分享到这啦
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.
本文介绍了散列表(哈希表)查找的概念,包括散列函数计算非负整数散列值的特点。讨论了两种常见的散列表构造方法,强调使用质数作为表长的重要性。详细阐述了开放地址法处理冲突,特别是线性探测法,并分析了不同冲突解决方法的平均查找长度。最后,给出了散列表的初始化、插入和查找操作的算法实现。总结了散列表适用于数据量小、装载因子小的情况,以及链表处理冲突的灵活性。
4442

被折叠的 条评论
为什么被折叠?



