哈希hash 各种用法最全详解

哈希hash

什么是哈希

  • 哈希表是一种散列表,可支持插入元素和查询元素的操作。
  • 当元素的取值范围特别大时,布尔数组的下标无法支持,这时可以用到哈希表。

操作

  • 对于一个哈希表,需要取一个固定的模数 p p p,哈希表的下标可以开到 p p p 1 − 2 1-2 12倍大,
  • 具体怎么用请往下看:
插入元素
  • 例如有如下元素 18 , 9 , 13 , 5 , 31 18,9,13,5,31 18,9,13,5,31,要把它们存入一个哈希表中,
  • 当前 p = 13 p=13 p=13
  • 放入 18 18 18,对 p p p取余,得到 5 5 5,那么就在 h a s h [ 5 ] = 18 hash[5]=18 hash[5]=18
  • 放入 9 9 9,对 p p p取余,得到 9 9 9,那么就在 h a s h [ 9 ] = 9 hash[9]=9 hash[9]=9
  • 放入 13 13 13,对 p p p取余,得到 0 0 0,那么就在 h a s h [ 0 ] = 13 hash[0]=13 hash[0]=13
  • 放入 5 5 5,对 p p p取余,得到 5 5 5,但 h a s h [ 5 ] hash[5] hash[5]有值了,那么下标右移,直到有空位为止, h a s h [ 6 ] = 5 hash[6]=5 hash[6]=5
  • 放入 31 31 31,对 p p p取余,得到 5 5 5,但 h a s h [ 5 ] hash[5] hash[5]有值了,那么下标右移,直到有空位为止( h a s h [ 6 ] hash[6] hash[6]也有值了), h a s h [ 7 ] = 18 hash[7]=18 hash[7]=18
void insert(int t)
{
	int x=t%p;
	while(hash[x])
	{
		x++;
		x%=maxn;//如果到了哈希表末尾那么就移动到开头
	}
	hash[x]=t;
}
查询元素
  • 若上面的元素插入完后,又有一些元素,我们需要判断它们是否在表中存在,
  • 2 , 31 , 5 , 6 2,31,5,6 2,31,5,6
  • 查询 2 2 2,对 p p p取余,得到 2 2 2 h a s h [ 2 ] = 0 hash[2]=0 hash[2]=0,那么说明不存在;
  • 查询 31 31 31,对 p p p取余,得到 5 5 5 h a s h [ 5 ] = 18 hash[5]=18 hash[5]=18,但不能说明不存在,要一直右移,每一位都判断一次,直到到了一个空位, h a s h [ 6 ] = 5 hash[6]=5 hash[6]=5 h a s h [ 7 ] = 31 hash[7]=31 hash[7]=31,发现存在,退出;
  • 查询 5 5 5,对 p p p取余,得到 5 5 5 h a s h [ 5 ] = 18 hash[5]=18 hash[5]=18 h a s h [ 6 ] = 5 hash[6]=5 hash[6]=5,发现存在,退出;
  • 查询 6 6 6,对 p p p取余,得到 6 6 6 h a s h [ 6 ] = 5 hash[6]=5 hash[6]=5 h a s h [ 7 ] = 31 hash[7]=31 hash[7]=31,直到 h a s h [ 8 ] = 0 hash[8]=0 hash[8]=0,没有发现存在,退出。
int find(int t)
{
	int x=t%p;
	while(hash[x])
	{
		if(hash[x]==t) return 1;
		x++;
		x%=maxn;
	}
	return 0;
}
  • 这样也挖掘出了哈希的一个新功能——判重!!!
插入与判重
  • 可以把两个合并,
  • 使它可以做到:如果返回是否存在,若不存在就插入!
int check(int t)
{
	int x=t%p;
	while(hash[x])
	{
		if(hash[x]==t) return 1;
		x++;
		x%=maxn;
	}
	hash[x]=t;
	return 0;
}
压缩
  • 如果要存入的不是一个数,而是一个数对,甚至一个字符串呢?
  • 当然可以,这时需要把各种千奇百怪的东西都压缩成一个数(归根结底还是变成了一个数)
  • 怎么压缩?
  • 尽可能使出现重复的概率少,
  • 如数对 ( 4 , 3 ) (4,3) (4,3)
  • 一种可以直接相乘,压缩成 12 12 12,但 ( 2 , 6 ) (2,6) (2,6)也是 12 12 12,会有重复,在哈希表里会耗更多时间;
  • 一种可以采用某种进制,若数对 ( x , y ) (x,y) (x,y)满足 x , y ∈ [ 0 , 9 ] x,yin[0,9] x,y[0,9]那么可以压缩成 4 ∗ 10 + 3 = 43 4*10+3=43 410+3=43,而且能压缩成 43 43 43的只有数对 ( 4 , 3 ) (4,3) (4,3),减少重复,效率就会增加。
  • 那么字符串若全是小写字母,则可以压缩成 27 27 27进制。

应用

记忆化搜索
  • 在搜索若想实现记忆化,但空间会爆炸的话,那么就可以用哈希!
  • 哈希表中每一位不仅存下压缩后的状态,还要存下对应的搜索出来的值。
  • 若不用哈希的搜索是这样的:
int dfs(int x,int y)
{
	if(x==1&&y==1) return 1;
	else
	{
		int s=0;
		for(int i=1;i<x;i++) s+=dfs(i,y-1);
		return s;
	}
}
  • 用了哈希实现记忆化就是这样的:
int check(int i,int y)
{
	int x=i*y%p;
	while(hash[x].use)
	{
		if(hash[x].i==i&&hash[x].y==y)
		{
			return x;
		}
	}
	return -x;//如果是负数代表没有找到!
}
void dfs(int x,int y)
{
	if(x==1&&y==1) return 1;
	else
	{
		int s=0;
		for(int i=1;i<x;i++)
		{
			int bz=check(i,y-1);
			if(bz>0) s+=hash[bz].ans;
			else 
			{
				int t=dfs(i,y-1);
				s+=t;
				bz=-bz;//取相反数变回原来的下标
				hash[bz].ans=t;
				hash[bz].i=i;
				hash[bz].y=y;
			}
		}
	}
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910081.html