哈希查找/散列查找(HashSearch)

哈希表/散列表:

1.什么是哈希表:

  • 哈希表不同于线性表数表之处在于其查找关键字(key)时不需要遍历表哈希表中的每一个元素都可以根据元素值计算出其存放的地址,从而达到查找时长为O(1)。
  • 这就好比" 字典",我们查找字典中的某个关键字(key)时,只需要查找目录即可找到该关键字(key)存放的页码(位置/地址),而查找的目录是有限的,且是线性的(常数阶/线性阶),所以无论字典存放的元素、关键字再多,我们也能通过目录快速定位查找到某个元素。
  • python中的 “字典(dict)” 就是如此,利用哈希表的存储结构。

2.基本概念和术语:

  1. 散列表:有限的、连续的存储空间,一般是数组。
  2. 散列函数:散列函数是函数,这个函数构建了与关键字(key)一 一对应的地址(address),不同的散列方法需要构造不同的散列函数,Hash(key)就是散列函数。
  3. 散列地址:由散列函数的到的地址就是散列地址,address=Hash(key)
  4. 冲突:key1≠key2; Hash(key1)==Hash(key2),即两个不同关键字,具有相同地址时,就叫发生冲突。
  5. 同义词:key1==key2,则key1key2互称为同义词。

散列法与散列函数的构造

1.散列函数的要求:

  1. 每一个 关键字(key) 只能有一个 地址(address) 与之对应。
  2. 函数值域必须在表长范围之内。
  3. 散列地址尽量均匀分布,尽可能的减少冲突。

2.散列法:

  1. 数字分析法:参考<数据结构p221>
  2. 平均取中法:参考<数据结构p222>
  3. 折叠法:参考<数据结构p222>
  4. 直接地址法:Hash(key)=keyHash(key)=a·kay+b
  5. 除留余数法:取关键值被某个不大于散列表长m的数p除后的所得的余数为散列地址。Hash(key)=key % P

处理冲突的方法:

1.开放地址法(闭散列法):

  • 核心思想是,把发生冲突的元素放到哈希表中的另外一个位置。
  1. 线性探测法:发生冲突时,逐位往后挪动,寻找合适位置,只要哈希表没满,就一定能找到一个不发生冲突的位置。
    addressi=( Hash(key) + di ),其中 di = 1,2,3···
关键字序列:(44、35、14、54)

散列函数: 
H(Key) = key MOD 10

处理冲突:线性探测

  1. 二次探测法:发生冲突时,每次向后挪动k2个单位(k为挪动次数)。
    addressi=( Hash(key) + di ),其中 di = 12,22,32···
关键字序列:(44、35、14、54)

散列函数: 
H(Key) = key MOD 10

处理冲突:二次探测

  1. 伪随机探测法:发生冲突时,每次向后挪动k个单位(k为伪随机生成数)。
  • 线性探测法的优点是:只要散列表未填满,总能找到一个不发生冲突的地址。缺点是:会产生 ”二次聚集“ 现象。而二次探测法和伪随机探测法的优点是:可以避免 “二次聚集“ 现象。缺点也很显然:不能保证一定找到不发生冲突的地址。

2.链地址法(开散列法):

  • 链地址法的基本思想是:把具有相同散列地址的记录放在同一个单链表中,称为同义词链表。有 m个散列地址就有 m个单链表,同时用数组 HT[0…m-1]存放各个链表的头指针,凡是散列地址为 i 的记录都以结点方式插入到以HT[i]为头结点的单链表中。
    在这里插入图片描述

散列表的平均查找长度(ASL):

1. 闭散列(开放地址):

关键字序列:(7、8、30、11、18、9、14)

散列函数: 
H(Key) = (key x 3) MOD 7

装载因子: 
0.7

处理冲突:线性探测
  • 数据7个,填装因子0.7,所以散列表长度=7/0.7=10,即数组下标0~9

① 查找成功:

  • 查找情况:
第一个元素7 -> 0 ;              (查找1次)
第二个元素8 -> 3 ;              (查找1次)
第三个元素30 -> 6 ;             (查找1次)
第四个元素11 -> 5 ;             (查找1次)
第五个元素18 -> 5 -> 6 -> 7 ;   (查找3次)
第六个元素9 -> 6 -> 7 -> 8 ;    (查找3次)
第七个元素14 -> 0 -> 1 ;        (查找2次)
  • 有7个数据元素,故有7种情况。
  • 所以ASLsucc=(1+1+1+1+3+3+2)/ 7 = 12/7

② 查找失败:

  1. 由散列函数获取首地址之后,再探测到空位置,即查找失败
  2. 探测完整个表,仍未找到,即查找失败。
  • 比较情况:
注:add表示哈希表地址

add[0] -> add[1] -> add[2](空)                           比较3次
add[1]-> add[2](空)                                      比较2次
add[2](空)                                               比较1次
add[3] -> add[4](空)                                     比较2次
add[4] (空)                                              比较1次
add[5] -> add[6] -> add[7] -> add[8] -> add[9](空)       比较5次
add[6] -> add[7] -> add[8] -> add[9](空)                 比较4次

只需看add[0] ~ add[6]
无需看add[7] ~ add[9]
  • 为什么只用看add[0] ~ add[6]
  1. 因为散列函数Hash(key)=(key x 3) % 7,中有个mod7,所以由散列函数求出来的初地址只能是0~6 ,故只有7种情况。
  • 所以ASLunsucc=(3+2+1+2+1+5+4)/ 7 = 18/7

2.开散列(链地址):

  • 计算ASL的方法与开散列类似。
关键字序列:(19、14、23、1、68、20、84、27、55、11、10、79)

散列函数: 
H(Key) = key MOD 13

处理冲突:链地址法

① 查找成功:

  • 每个单链表中的第 1 个结点的关键字(如 14、 68、 19、 20、 23、 11), 查找成功时只需比较 1 次,而对千第 2个结点的关键字(如 1、 55、 84、 10), 查找成功时需比较 2 次, 第 3 个结点的关键字 27 需比较 3 次, 第4个结点的关键字 79 则需比较4次才能查找成功。这时,查找成功时的平均查找长度为:ASLsucc=(1·6 + 2·4+3+4)/ 12

② 查找失败:

  • 采用链地址法处理冲突时,待查的关键字不在表中,若计算散列函数 H(key)= 0, HT[0]的指 针域为空,比较1 次即确定查找失败。若H(key)= 1, HT[l]所指的单链表包括4个结点,所以需要比较5次才能确定失败。类似地, 对H(key)= 2, 3, …,12 进行分析,可得查找失败的平均查找 长度为:ASLunsucc= (1+5 +1+3 +1+1+3+2+1+1+3+2+1)/ 13

3.ASL计算总结:

① 查找成功:

② 查找失败:


完整源代码

#include <stdio.h>
#include <stdlib.h>

#define dataType int
#define HashSize 12
#define Empty -32767

typedef struct
{
	dataType *value;
	int hashsize;
}HashMap; 

void IntiHashMap(HashMap *H)
{
	int i;
	
	H->hashsize=HashSize; //初始化哈希表长度 
	H->value=(dataType *)malloc(HashSize*sizeof(dataType)); //创建哈希表数组 
	for(i=0;i<HashSize;i++) //初始化哈希表为空 
	{
		H->value[i]=Empty;
	} 
}

//散列函数 (除留余数法) 
int Hash(dataType value) 
{
	return value%HashSize; //除数也可换成其他更合适的值,如小于HashSize的最大质数 
}

void Insert(HashMap *H,int data)
{
	int InsertAddress;
	
	InsertAddress=Hash(data);
	
	//发生冲突 
	while(H->value[InsertAddress]!=Empty)//线性探测 
	{
		InsertAddress=(InsertAddress+1)%HashSize;
	}
	H->value[InsertAddress]=data;
} 

int Search(HashMap *H,int SeartData)
{
	int HashAddress;
	
	HashAddress=Hash(SeartData);
	
	while(H->value[HashAddress]!=SeartData)
	{
		HashAddress=(HashAddress+1)%HashSize;
		
		if(H->value[HashAddress]==Empty||HashAddress==Hash(SeartData))
		{
			return -1;
		}
	}
	
	return HashAddress;
}


int main() 
{
	HashMap H;
	int i;
	int a[12]={12,67,56,16,25,37,22,29,15,47,48,34};

	IntiHashMap(&H); 
	
	for(i=0;i<HashSize;i++)
	{
		Insert(&H,a[i]);
	}
	
	printf("address   ");
	for(i=0;i<HashSize;i++)
	printf("%d\t",i);
	printf("\n\n");
	
	printf("a[i]     ");
	for(i=0;i<HashSize;i++)
	printf("%d\t",a[i]);
	printf("\n\n");
	
	printf("Hash[i]  ");
	for(i=0;i<HashSize;i++)
	printf("%d\t",H.value[i]);
	printf("\n\n");
	
	printf("a[6]的地址是:");
	printf(" %d",Search(&H,a[6]));
	
	return 0;
}

执行结果:

address  0  1  2  3  4  5  6  7  8  9  10 11

a[i]     12 67 56 16 25 37 22 29 15 47 48 34

Hash[i]  12 25 37 15 16 29 48 67 56 34 22 47

a[0]的地址是: 0
a[1]的地址是: 7
a[2]的地址是: 8
a[3]的地址是: 4
a[4]的地址是: 1
a[5]的地址是: 2
a[6]的地址是: 10
a[7]的地址是: 5
a[8]的地址是: 3
a[9]的地址是: 11
a[10]的地址是: 6
a[11]的地址是: 9
--------------------------------
Process exited after 0.01566 seconds with return value 0
请按任意键继续. . .

参考:

  1. 数据结构C语言第二版—严蔚敏
  2. 哈希表(散列表)详解
  3. Hash表的平均查找长度ASL计算方法
  • 10
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
DS哈希查找-二次探测再散列是一种解决哈希冲突的方法。它使用二次探测再散列技术来解决冲突,即当发生冲突时,通过一定的算法找到下一个可用的位置。 以下是DS哈希查找-二次探测再散列的步骤: 1. 定义哈希函数:首先,需要定义一个哈希函数,将关键字映射到哈希表的位置。在这个例子中,哈希函数为H(key) = key%11,即将关键字除以11取余数。 2. 创建哈希表:根据输入的表长,创建一个具有相应大小的哈希表。 3. 插入关键字:将输入的关键字集合插入到哈希表中。如果发生冲突,即两个关键字映射到了同一个位置,就使用二次探测再散列来找到下一个可用的位置。 4. 查找给定关键字:根据给定的关键字,使用哈希函数计算出它在哈希表中的位置。如果该位置上的关键字与给定关键字相等,则找到了;否则,使用二次探测再散列找到下一个位置,直到找到相等的关键字或者遇到空位置。 下面是一个示例代码,演示了DS哈希查找-二次探测再散列的过程: ```python def hash_function(key, table_size): return key % table_size def insert(hash_table, key, table_size): index = hash_function(key, table_size) if hash_table[index] is None: hash_table[index] = key else: i = 1 while True: new_index = (index + i*i) % table_size if hash_table[new_index] is None: hash_table[new_index] = key break i += 1 def search(hash_table, key, table_size): index = hash_function(key, table_size) if hash_table[index] == key: return index else: i = 1 while True: new_index = (index + i*i) % table_size if hash_table[new_index] == key: return new_index elif hash_table[new_index] is None: return -1 i += 1 # 创建哈希表 table_size = 11 hash_table = [None] * table_size # 插入关键字 keys = [12, 23, 34, 45, 56, 67, 78, 89, 90] for key in keys: insert(hash_table, key, table_size) # 查找给定关键字 search_key = 34 result = search(hash_table, search_key, table_size) if result != -1: print("关键字 {} 在哈希表中的位置是 {}".format(search_key, result)) else: print("关键字 {} 不在哈希表中".format(search_key)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Attract1206

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值