• 哈希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;
    			}
    		}
    	}
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    图片处理帮助类
    网站前端的优化
    状态码大全
    服务端性能监控工具Nmon使用方法
    jmeter测试报告解析
    jmeter聚合报告
    jmeter跨线程组调用参数
    jmeter生成测试报告
    Jmeter获取cookie
    monkey的使用方法和常见命令
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910081.html
Copyright © 2020-2023  润新知