数据结构之哈希表
来源:互联网 发布:mac粉 编辑:程序博客网 时间:2024/05/22 15:39
小目录
1.什么是哈希表
2.哈希表函数的构造方法
3.处理冲突的方法
4.哈希表的查找及其分析
1.什么是哈希表
哈希表是种数据结构,它可以提供快速的插入操作和查找操作。第一次接触哈希表时,它的优点多得让人难以置信。不论哈希表中有多少数据,插入和删除(有时包括侧除)只需要接近常量的时间即0(1)的时间复杂度.那么怎么进行哈希呢?即选取某个函数,依该函数按关键码计算元素的存储位置,并按此存放;查找时,由同一个函数对给定值kx 计算地址,将key与地址单元中元素关键码进行比,确定查找是否成功,这就是哈希方法;哈希方法中使用的转换函数称为哈希函数;按这个思想构造的表称为哈希表。
2.哈希表函数的构造方法
由于哈希表的构造方法比较多,这里做简单的摘要(除留余数法最常用)。
一. 直接定址法
Hash(key)=a·key+b (a、b 为常数)即取关键码的某个线性函数值为哈希地址,这类函数是一一对应函数,不会产生冲突,但要求地址集合与关键码集合大小相同,因此,对于较大的关键码集合不适用。二. 除留余数法
Hash(key)=key mod p (p 是一个整数)即取关键码除以p 的余数作为哈希地址。使用除留余数法,选取合适的p 很重要,若哈希表表长为m,则要求p≤m,且接近m 或等于m。p 一般选取质数,也可以是不包含小于20 质因子的合数。三. 乘余取整法
Hash(key)= ⎣B*(A*key mod 1)⎦ (A、B 均为常数,且0<A<1,B 为整数)以关键码key 乘以A,取其小数部分(A*key mod 1 就是取A*key 的小数部分),之后再用整数B 乘以这个值,取结果的整数部分作为哈希地址。该方法B 取什么值并不关键,但A 的选择却很重要,最佳的选择依赖于关键码集合的特征四. 数字分析法
设关键码集合中,每个关键码均由m 位组成,每位上可能有r 种不同的符号五. 平方取中法
对关键码平方后,按哈希表大小,取中间的若干位作为哈希地址六. 折叠法(Folding)
此方法将关键码自左到右分成位数相等的几部分,最后一部分位数可以短些,然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。这种方法称为折叠法。 3.处理冲突的方法
1. 开放寻址法:
Hi=(H(key) + di) MOD m, i=1,2,…, k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:
1. di=1,2,3,…, m-1,称线性探测再散列;
2. di=1^2, (-1)^2, 2^2,(-2)^2, (3)^2, …, ±(k)^2,(k<=m/2)称二次探测再散列;
3. di=伪随机数序列,称伪随机探测再散列。 ==
2. 再散列法:
Hi=RHi(key), i=1,2,…,k RHi均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。
3. 链地址法(拉链法)
当存储结构是链表时,多采用拉链法,用拉链法处理冲突的办法是:把具有相同散列地址的关键字(同义词)值放在同一个单链表中,称为同义词链表。有m个散列地址就有m个链表,同时用指针数组T[0..m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以T[i]为指针的单链表中。4. 建立一个公共溢出区
设哈希函数产生的哈希地址集为[0,m-1],则分配两个表:一个基本表ElemType base_tbl[m];每个单元只能存放一个元素;一个溢出表ElemType over_tbl[k];只要关键码对应的哈希地址在基本表上产生冲突,则所有这样的元素一律存入该表中。4.哈希表的查找及其分析
哈希表的查找过程基本上和造表过程相同。一些关键码可通过哈希函数转换的地址直接找到,另一些关键码在哈希函数得到的地址上产生了冲突,需要按处理冲突的方法进行查找。在介绍的三种处理冲突的方法中,产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以,对哈希表查找效率的量度,依然用平均查找长度来衡量。
下面给出具体的算法(该算法哈希构造使用除留余数法,冲突采用开放地址法中的线性探测,相关的函数在源码中):
/** @description:在哈希表中查找,主要配合插入操作* @more:在开放地址哈希表中查找关键词,若查找成功, 以p指示待查数据在表中的位置,并且返回查询成功 若查询失败,则返回应该插入位置*/Status SearchHash(HashTable H,KeyType key,int *p, int *c) { //求哈希地址 *p = Hash(key); //向插入的位置已经有元素且不等于插入的关键词本身 while(H.elem[*p].key != NULLKEY && H.elem[*p].key != key) { (*c)++; if((*c) < m) //产生冲突,则探寻下一个可能的位置 collision(p,*c); else break; } //查找成功 if(H.elem[*p].key == key) return SUCCESS; else return UNSUCCESS;}/** @description:求哈希值* @more:可根据实际情况来选择更合适的*/unsigned Hash(KeyType key) { return key % m;}/** @description:冲突处理函数,这里使用开放地址法*/void collision(int *p,int c) { *p = (*p + c) % m;}/** @description:哈希表插入元素*/Status InsertHash(HashTable *H,KeyType key) { int c,p; //用于计数,也用作步长 c = 0; if(SearchHash(*H,key,&p,&c)) return DUPLICATE; /* 产生冲突但是冲突次数没有达到上限 这里的上限是可以调整,根据实际情况来设定 */ else if(c < hashsize[(*H).sizeindex] / 2) { //插入 (*H).elem[p].key = key; (*H).count++; return SUCCESS; } //冲突次数超过了上限,故需要重建哈希表即进行扩容 else { RecreateHash(H); return UNSUCCESS; }}/** @description:重建哈希表,即进行扩容*/void RecreateHash(HashTable *H) { int i,count; ElemType *p,*elem; count = (*H).count; elem = (ElemType *) malloc (count * sizeof(ElemType)); if(!elem) exit(OVERFLOW); //将现在哈希表中的元素暂存到elem中 for(i = 0 ; i < m ; i++) if((*H).elem[i].key != NULLKEY) *elem = *((*H).elem + i); (*H).count = 0; (*H).sizeindex++; m = hashsize[(*H).sizeindex]; p = (ElemType *) realloc ((*H).elem, m * sizeof(ElemType)); if(!p) exit(OVERFLOW); (*H).elem = p; //初始化新哈希表中元素值 for(i = 0; i < m ; i++) (*H).elem[i].key = NULLKEY; //将元素插入到新的哈希表中 for(p = elem ; p < elem + count ; p++) InsertHash(H,(*p).key);}
查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:
- 哈希函数是否均匀
- 处理冲突的方法
- 哈希表的装填因子
可知,其时间复杂度为O(1)
蛋疼的上一张测试截图:
附上源码地址:GitHub
- 数据结构之哈希表
- 数据结构之哈希表
- 数据结构之哈希表(4)
- 数据结构之哈希表
- 数据结构之哈希表
- 数据结构之哈希表
- 数据结构之哈希表
- 数据结构之哈希表
- 数据结构之哈希表
- 数据结构之哈希表
- 7.数据结构之哈希表
- 数据结构之 哈希表
- 数据结构之哈希表
- 计蒜客:数据结构之哈希表
- 数据结构算法之哈希表
- 数据结构之哈希表
- 数据结构之哈希表
- 数据结构之哈希表
- LIBCMT.lib与msvcrtd.lib冲突的链接错误
- 1.数据结构树结构的应用
- 数据库连接方式改进
- 个人学习总结一Java的多线程
- Android加载图片导致内存溢出(Out of Memory异常)
- 数据结构之哈希表
- ubuntu 12.04 常用配置
- wav与pcm数据
- 使用指针互换两个实参的值
- iOS 7 自定义Back按钮 与 Pop interactive gesture 问题
- Linux awk命令详解
- PCM数据合成WAV文件
- SQL 如何 远程备份数据库到本地
- UIPageViewController-浅析