1月13日自习总结

今天学了哈希表查找

哈希表

哈希算法:将一个值或一个数组,通过一定的规则转换成另一个值

哈希表的实现和应用

hash表又叫散列表,是一种用来存放数据的数据结构。用于快速查询

hash表就是一种数组,输入关键字,通过hash函数得到,对应数据的下标。hash值就是下标)

散列函数构造方法

1.直接定址法

2.数字分析法

3.平方取中法

4.折叠法

5.除留余数法

 

最简单的哈希思想(与桶的思想有点类似):

如在长度为10的s数组里面存储着1~100范围的数,先开辟一个大小为100的t数组,初始化为0,遍历一遍数组s,借助t数组统计s数组中的各个数出现的次数,然后查找,比如说查找数3是否在数组s里面,只要看t[3]的值是否大于0即可

 

对于整数数据直接取余表长得到对应的哈希值,字符串需要专门的哈希函数进行转换

将转换后得到的值作为下标,将哈希表中对应下标的存值加1,哈希函数可能将不同的数据映射到同一个数组下标上,这样就发生了冲突导致查询时出现错误

 

哈希冲突:

哈希冲突:不同的关键字通过同一个哈希函数可能得到同一哈希地址,即 key1 ≠ key2,而

Hash(key1) = Hash(key2),这种现象称为哈希冲突。

解决:

1.开放地址法

指的是将哈希表中的「空地址」向处理冲突开放。当哈希表未满时,处理冲突时需要尝试另外的

单元,直到找到空的单元为止

H(i) = (Hash(key) + F(i)) \% m,i = 1, 2, 3, ..., n (n ≤ m - 1)

2.链地址法

(用一个二维数组第一行来存储哈希值,当某列的哈希值有冲突的话就将冲突的哈希值存放到该列的下一行????)

将具有相同哈希地址的元素(或记录)存储在同一个线性链表中。 链地址法是一种更加常用的哈希冲突解决方法。相比于开放地址法,链地址法更加简单。 假设哈希函数产生的哈希地址区间为 [0, m - 1],哈希表的表长为 m。则可以将哈希表定义为一个有 m 个头节点组成的链表指针数组 T。

这样在插入关键字的时候,只需要通过哈希函数 Hash(key) 计算出对应的哈希地址 i,然后将其以链表节点的形式插入到以 T[i] 为头节点的单链表中。在链表中插入位置可以在表头或表尾,也可以在中间。如果每次插入位置为表头,则插入操作的时间复杂度为 O(1)。

    而在在查询关键字的时候,只需要通过哈希函数 Hash(key) 计算出对应的哈希地址 i,然后将对应位置上的链表整个扫描一遍,比较链表中每个链节点的键值与查询的键值是否一致。查询操作的时间复杂度跟链表的长度 k 成正比,也就是 O(k)。对于哈希地址比较均匀的哈希函数来说,理论上讲,k= n//m,其中 n 为关键字的个数,m 为哈希表的表长。

 代码实现:

#include<bits/stdc++.h>
#define Max 11
#define N 8
int hashtable[Max];//已知hash表的长度为11
int func(int value) { //哈希函数返回哈希值
	return value % Max;
}
int search(int key) { //哈希查找
	int pos, t;
	pos = func(key); //确定储存位置
	t = pos; //用t存放pos
	while (hashtable[t] != key && hashtable[t] != -1) {
		t = (t + 1) % Max; //线性探测法求出下一个位置
		if (pos == t) { //经过多次探测后又回到原位置,说明要查找的随机数不存在
			return -1;
		}
	}
	if (hashtable[t] == -1) { //该位置没有随机数,查找失败
		return -1;
	} else {
		return t;//查找成功
	}
}
void creathash(int key) { //自定义函数实现哈希表的创建
	int pos, t;
	pos = func(key); //查找随机数和哈希表的创建都都利用了哈希函数
	t = pos;
	while (hashtable[t] != -1) { //该位置右随机数,利用线性探测寻找下一个位置
		t = (t + 1) % Max;
		if (pos == t) { //经过多次探测后又回到原来位置,说明位置已满,即位置上都有随机数
			printf("哈希表已满!\n");
			return;
		}
	}
	hashtable[t] = key; //将创建的随机数放入哈希表中
}
int main() {
	int flag[50] = {0}; //定义标记变量并初始为0
	int i, j, t;
	memset(hashtable, -1, sizeof(hashtable));
	srand((unsigned long)time(0));//以系统时间为种子产生随机数
	i = 0; //i从0累加到7,代表着要随机产生8个小于50的随机数赋给t
	while (i != N) {
		t = rand() % 50; //产生一个50以内的随机数
		if (flag[t] == 0) { //说明t是50以内的随机数且是由上一步产生的
			creathash(t);//调用函数创建哈希表且将t存在表中,表长为11
			printf("%2d:", t); //输出随机数
			for (int j = 0; j < Max; j++) { //输出哈希表内容
				printf("%2d  ", hashtable[j]);
			}
			printf("\n");
			flag[t] = 1; //产生一个随机数就将flag改为1,flag[t]和t是对应的
			i++;//准备产生下一个随机数
		}
	}//将8个随机数存入哈希表
	printf("开始查找:\n");
	scanf("%d", &t);
	if (search(t) != -1) {
		printf("该数在%d\n", search(t));
	} else {
		printf("该数不存在\n");
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值