《算法笔记》学习笔记

第章4入门篇(2)–算法初步(2)

4.2 散列

  1. 散列的定义与整数散列
    散列:将元素通过一个函数转换为整数,使得该整数可以尽量唯一地代表这个元素。其中把这个转换函数称为散列函数H,也就是说,如果元素在转换前为key,那么转换后就是一个整数H(key).
    常用的散列函数有:直接定址法、平方取中法、除留余数法。
    直接定址法:指恒等变换(即H(key)=key,直接把key作为数组下标,是最常见最实用的散列应用)
    平方取中法:指取key的平方的中间若干位作为hash值(很少用)。
    除留余数法:指把key除以一个数mod得到的余数作为hash值的方法,即H(key)=key%mod。通过这个散列函数,可以把很大的数转换为不超过mod的整数这样就可以将它作为可行的数组下标。当mod是一个素数时,H(key)能尽可能覆盖[0,mod)范围内的每一个数。
    通过除留余数法可能会有两个不同的数key1和key2,它们的hash值H(key1)与h(key2)是相同的,这样当key1已经把表中位置为H(key1)的单元占据时,key2便不能使用这个位置,这样便产生了“冲突”。下面有三种方法来解决冲突:
    (1)线性探查法(Linear Probing)
    当得到key的hash值H(key),但是表中下标为H(key)的位置已经被某个其他元素使用了,那么就检查下一个位置 H(key)+ 1是否被占,如果没有,就使用这个位置: 否则就继续检查下一个位置(也就是将hash值不断加1)。如果检查过程中超过了表长,那么就回到表的首位继续循环,直到找到一个可以使用的位置,或者是发现表中所有位置都已被使用。显然,这个做法容易导致扎堆,即表中连续若千个位置都被使用,这在一定程度上会降低效率。
    (2)平方探查法(Quadratic probing)
    在平方探查法中,为了尽可能避免扎堆现象,当表中下标为H(key的位置被占时,将按下面的顺序检查表中的位置: H(key)+ 12、H(key)- 12、H(key)+22、H(key)- 22、H(key) +32、…如果检查过程中H(key)+k2超过了表长TSize,那么就把H(key)+k2对表长TSize取模:如果检查过程中出现H(key) - k2< 0的情况(假设表的首位为0),那么将((H(key)- k2)% TSize+ TSize) % TSize作为结果(等价于将H(key) - k2不断加上TSize直到出现第一个非负数)。如果想避免负数的麻烦,可以只进行正向的平方探查。可以证明,如果k在[0, TSize)范围内都无法找到位置,那么当k≥TSize时,也一-定无法找到位置。
    (3)链地址法(拉链法)
    和上面两种方法不同,链地址法不计算新的hash值,而是把所有H(key)相同的key连接成一条单链表。这样可以设定一个数组Link, 范围是Link[0]~ Link[mod -1], 其中Link[h]存放H(key)= h的一条单链表,于是当多个关键字key的hash值都是h时,就可以直接把这些冲突的key直接用单链表连接起来,此时就可以遍历这条单链表来寻找所有H(key)= h的key。
    当然,一般来说, 可以使用标准库模板库中的map 来直接使用hash的功能(C++11以后可以用unordered_map,速度更快),因此除非必须模拟这些方法或是对算法的效率要求比较高,一般不需要自己实现上面解决冲突的方法。

  2. 字符串hash初步
    字符串hash是指一个字符串S映射为一个整数,使得该整数可能尽可能唯一地代表字符串S。

给出N个字符串(由恰好三位大写字母组成),再给出M个查询字符串,问每个查询字符串在N个字符串中出现的次数。

#include<iostream>
using namespace std;
const int maxn=100;
char S[maxn][5],temp[5];
int hashTable[26*26*26+10];
int hashFunc(char S[],int len){
	int id=0;
	for(int i=0;i<len;i++){
		id=id*26+(S[i]-'A');
	}
	return id; 
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%s",S[i]);
		int id=hashFunc(S[i],3);
		hashTable[id]++;
	}
	for(int i=0;i<m;i++){
		scanf("%s",temp);
		int id=hashFunc(temp,3);
		printf("%d\n",hashTable[id]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值