Hash查找


Hash查找是一种效率很高的查找方法,以下将Hash查找的hash函数设计方法,以及冲突解决方法列举:


hash函数设计:

(1)直接定址法:
H(key)=key 或者 H(key)=a*key+b
适用于地址集合的大小==关键字集合的大小

(2)除留余数法:
H(key)=key Mod p
其中模p的大小满足:p为不大于M的素数 或者 不含20一下的质因子

int h(int x) { return(x % 16); }

(3)平方取中法:
以关键字的平方值的中间几位作为存 储地址。求“关键字的平方值”的目的 是“扩大差别”,同时平方值的中间各 位又能受到整个关键字中各位的影响
此方法适合于: 关键字中的每一位都有某些数字重复 出现频度很高的现象

(4)折叠法:
将关键字分割成若干部分,然后取它 们的叠加和为哈希地址。有两种叠加处 理的方法:移位叠加和间界叠加。
此方法适合于: 关键字的数字位数特别多。

int h(char* x) 
{ 
	int i, sum; 
	for (sum=0, i=0; x[i] != '\0'; i++) 
	sum += (int) x[i]; 
	return(sum % M); 
}

(5)数字分析法
假设关键字集合中的每个关键字都是 由 s 位数字组成 (u1, u2, …, us),分析关 键字集中的全体,并从中提取分布均匀 的若干位或它们的组合作为地址
此方法仅适合于: 能预先估计出全体关键字的每一位 上各种数字出现的频度。

int ELFhash(char* key)
 { 
 unsigned long h = 0; 
 while(*key) 
 {
	  h = (h << 4) + *key++;//使得不同位置的字符权值不同 
	  unsigned long g = h & 0xF0000000L; 
	  if (g) h ^= g >> 24; 
	  h &= ~g; 
  } 
	 return h % M; 
  }

(6)随机数法:
H(key) = Random(key) 其中,Random 为伪随机函数
通常,此方法用于对长度不等的关键 字构造哈希函数。


hash函数冲突解决:

处理冲突的方法一般有两种:
(1)闭散列方法(开地址方法):把冲突记录存储在表中另一个槽中。 “内部问题内部解决”
(2)开散列方法(单链方法):吧冲突记录存储在表外,内部问题外部解决。

闭散列方法:
• 把所有记录直接存储在散列表中
• 每条记录i有一个基位置h(ki),即根据散列函数算出来的槽
• 当要插入一条记录R时,如计算出来的基位置已经被 另一条记录占据,则根据冲突解决策略来决定把R存储在表中的其他槽内;
• 检索方法也相似,先根据关键字值计算基位置,当未找到时再根据冲突解决策略在表中其他相应槽中来寻找
闭散列方法主要有2种:
(1) 桶式哈希:
在这里插入图片描述
桶式哈希的查询
• 当检索某条记录时,首先计算哈希函数确定记
录所在的桶,然后在该桶中检索记录。 • 若在该桶中找到记录,则返回成功; • 若在该桶中没有找到记录,且该桶未满,则返
回未查找到该记录; • 若在该桶中没有找到记录,但该桶已满,则还
需检索溢出桶,直到找到记录或者溢出桶的所
有记录都已被检索为止。

(2)探查序列方法:
为冲突的地址H(key)求一个地址序列:
H0,H1,H2,…Hs 1<=s<=m-1 其中H0=H(key)
Hi=(H(key)+di)%M, i=1,2,3,…

对增量di的三种取法:
线性探测再散列
d i = c i 最简单的情况 c=1
探查函数是p(K,i) = i
c

平方探测再散列
d i = 1^ 2 , - 1^2 ,- 2^2 , …
探查函数是
p(K,2i-1) = i * i
p(K,2i) = - i * i

◼ 随机探测再散列
d i 是一组伪随机数列 或者 d i =i × H 2 (key) (又称双散列函数探测)
探查函数P(k,i)=rand() 或P= i*H2(key)

开散列方法
将所有哈希地址相同的记录 都链接在同一链表中。
在这里插入图片描述
算法思想

开散列的查询和构造一致,查找过程则有:
对于给定值 K,计算哈希地址 i = H(K) 
若 r[i] = NULL  则查找不成功 
若 r[i].key = K  则查找成功 
否则求下一地址 Hi:
直至 r[Hi] = NULL  (查找不成功) 或 r[Hi].key = K  (查找成功) 为止。

具体实现

// Insert e into hash table HT 
template <typename Key, typename E> bool hashdict<Key, E>:: hashInsert(const key& e, const E& e) 
{ 
	int home; // Home position for e 
	int pos = home = h(k); // Init 
	for (int i=1; EMPTYKEY!=(HT[pos].key(); i++) 
	{ 
		pos = (home + p(k,i)) % M; 
		Assert(k!= HT[pos].key(),”Duplicates not allowed”); 
	} 
	Kvpair<Key,E> temp(k,e); 
	HT[pos] = temp;   // Insert e
}

// Search for the record with Key K 
template <typename Key, typename E> E hashdict<Key, E>:: hashSearch(const Key& k) const 
{ 
	int home; // Home position for K 
	int pos = home = h(k); // Initial posit 
	for (int i = 1; (k!=( HT[pos].key()) && (EMPTYKEY!=( HT[pos]).key()); i++) 
	{
		pos = (home + p(k, i)) % M; // Next 
		if (k==(HT[pos]).key()) 
		{ // Found it 
			return( HT[pos]).value(); 
		}
		else return NULL; // K not in hash table 
	}
}

散列方法的效率分析
◼ 衡量标准:插入、删除和检索操作所需要的记录访 问次数
◼ 散列表的插入和删除操作都是基于检索进行的
◼ 删除:必须先找到该记录
◼ 插入:必须找到探查序列的尾部,即对这条记录进行 一次不成功的检索
◼ 对于不考虑删除的情况,是尾部的空槽
◼ 对于考虑删除的情况,也要找到尾部,才能确定是 否有重复记录

哈希表查找的分析:
◼ 选用的哈希函数;
◼ 选用的处理冲突的方法;
◼ 哈希表饱和的程度,装载因子α=n/m 值的大小 (n—记录数,m—表的长度) 决定哈希表查找的ASL的因素: 哈希表查找的分析 一般情况下,可以认为选用的哈希函数是“均匀” 的,则在讨论ASL时,可以不考虑它的因素。
因此,哈希表的ASL是处理冲突方法和装载因子的 函数

影响检索的效率的重要因素
◼ 散列方法预期的代价与负载因子 α= N/M有关
◼ α 较小时,散列表比较空,所插入的记录比较容易插 入到其空闲的基地址
◼ α 较大时,插入记录很可能要靠冲突解决策略来寻找 探查序列中合适的另一个槽
◼ 随着α增加,越来越多的记录有可能放到离其基地 址更远的地方

查找成功时有下列结果
在这里插入图片描述
内容来自湖南大学数据结构课件

发布了100 篇原创文章 · 获赞 56 · 访问量 4861

猜你喜欢

转载自blog.csdn.net/weixin_44307065/article/details/103796738
今日推荐