Hash 哈希表和算法思路详解
概述
- 哈希表是一种可以满足快速查找数据结构,时间复杂度接近O(1)。
- 哈希函数是无限集到有限集的映射。
- 处理数据量大,查找效率要求高时推荐使用hash容器。
- 问题:
- 什么情况下考虑使用哈希容器?
- 常用的哈希思路有哪些?
- 评判哈希算法标准有哪些?
- 哈希冲突是如何产生的?如何解决?
- 如何构造一个hash算法?应注意哪些问题?
评判哈希算法标准
- 效率高。
- 映射分布均匀。
基础hash思路
直接寻址法:
取关键字key,使用线性函数 Hash(key) = a * key + b。
数字分析法:
在一个班级里,同龄学生很多。在取学生年龄作为key时,应避免以年份作为key组成部分。
平方取中法:
key取平方,截取中间的几位作为新的key。数学计算的性质乘积中间几位和乘数每一位都有关,充分混合key每一位对生成的哈希值的影响,使映射分布更均匀。
取余法:
Hash(key) = key % m
相乘取整法:
Hash(key) = floor(frac(key * A), m), 0<A<1
- floor 取整,frac 取小数
- 此法避免像除余法中结果对m过于依赖。
随机数法
Hash(key) = rand(key)
- 据我所知C#的object采用此方法,使用元数据中的几位存hash值。
折叠法:
将关键字按固定长度分成几段然后相加。
- 如:Hash(1234,m = 2) = 46。
- 关键字较长时可以考虑使用此方法。
哈希冲突
产生原因
由于哈希函数是无限集到有限集的映射,换而言之,有限集的元素对应n个无限集的元素,哈希碰撞是不可避免的。
解决办法
开放地址法
当关键字key的哈希地址p=H(key)出现冲突时,递归调用p = Hi(p)直到没有冲突。
Hi=(H(key)+di)Hi=(H(key)+di) % m i=1,2,,3....,ni=1,2,,3....,n
- H(key) 为哈希函数
- m 为表长
- di 为增量序列
根据增量序列di的不同,又分为:
- 线性探测:di = 1,2,3,......
- 二次探测: di = ±1^2, ±2^2,.......
- 随机探测: di = random(di,seed)
- random 为 无状态的伪随机发生函数(所谓无状态,即无论多少次调用,random(a) = b不变)
- seed 一个确定不变的随机数种子
链式地址法
结构示意
pos1
pos2 -> val -> val
pos3 -> val
pos4
...
无限集映射到有限集,有限集的每个元素对应一个链表,链表存储无限集映射到有限集的n个元素。
再哈希法
Hi=RHi(key)i=1,2,…,k
递归调用哈希函数序列中的函数,直到没有冲突。
建立公共溢出区法
建立溢出链表,如发生哈希碰撞,则使用溢出链表。
哈希冲突解决方法优缺点分析
开放散列:链式地址法(桶链法)
- 优点:
- 添加删除方便,避免动态调整开销
- 桶链表内存动态分配,减少内存浪费
- 当哈希表size很大时,指针的性能消耗可以忽略
- 缺点:
- 动态分配内存,内存不紧凑,随机访问性差,序列化性能差。
- 对于预先知道所有元素,可以实现没有冲突的完美hash函数,此时效率会远低于封闭散列。
封闭散列:开放地址法,再哈希法 ...
- 优点:
- 内存紧凑,随机访问性能好,序列化性能好。
- 预先知道所有元素e,可以实现完美hash函数,此时效率远高于开放散列。
- 缺点:
- 所有条目数量不能超过数组的长度,扩容/收紧频繁,性能消耗大。
- 碰撞探测消耗性能。
- 当数组长度很大时,有内存浪费。
哈希算法进阶实例分析
这是取自lua5.4的
-- lua 5.4
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed,
size_t step) {
unsigned int h = seed ^ cast_uint(l);
for (; l >= step; l -= step)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
return h;
}
#define lmod(s,size) \
(check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1)))))
(h << 5) + (h >> 2)
= (((h << 5) << 2) + ((h >> 2) << 2) >> 2)
= ((h << 7) + h) >> 2
= (129 * h) >> 2
和伪随机数生成算法一样,要让生成的数尽量随机--二进制数的每一个位取0或1的概率都是50%。
移位,异或运算充分混合每一位的影响,而加法运算引起多个位的反转,使hash值的每一个位更加不可预测,以接近不可逆的单向函数。
(h << 5) + (h >> 2) = (129 * h) >> 2。 乘法可以被拆分为加法和移位的组合(即(h << 7)+h ),以混合哈希值。不过(h << 7 - h) = 127h 会更好些,127是梅森素数(2^n -1)。与线性同余算法(LCG)生成伪随机数一样,梅森素数127,只需一次移位运算和一次加法运算,且不会被分解,随机数分布更加均匀。

- 非素数会被分解成更小的素数的乘积,参与运算时容易被分解,上例中a和c可以提取公因数d,周期 = n = c/d。
a%b = a&(b-1) 当 b = 2^n 时等式成立,lua哈希表的长度保证符合等式成立的条件,lmod使用位运算代替取余运算,效率更高。
算法实际应用详情请参考我的文章
进阶哈希算法
下面是一些进阶哈希算法的思路,需要花费一些时间学习。
Hash 哈希表和算法思路详解的更多相关文章
- C#中哈希表(HashTable)的用法详解以及和Dictionary比较
1. 哈希表(HashTable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似keyvalue的键值对, ...
- C#中哈希表(HashTable)的用法详解
描述: 哈希表存放 key.values ,key值可以用于快速调取用,values 对应object类型,也就是说所有类型. 实例: 1.HashTable存放学生的成绩 Hashtable ht1 ...
- 转 C#中哈希表(HashTable)的用法详解
看了一遍有关哈希表的文字,作者总结的真是不错 .收藏起来 1. 哈希表(HashTable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提 ...
- javascript常用经典算法实例详解
javascript常用经典算法实例详解 这篇文章主要介绍了javascript常用算法,结合实例形式较为详细的分析总结了JavaScript中常见的各种排序算法以及堆.栈.链表等数据结构的相关实现与 ...
- CRF(条件随机场)与Viterbi(维特比)算法原理详解
摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...
- 各大公司广泛使用的在线学习算法FTRL详解
各大公司广泛使用的在线学习算法FTRL详解 现在做在线学习和CTR常常会用到逻辑回归( Logistic Regression),而传统的批量(batch)算法无法有效地处理超大规模的数据集和在线数据 ...
- MD5算法步骤详解
转自MD5算法步骤详解 之前要写一个MD5程序,但是从网络上看到的资料基本上一样,只是讲了一个大概.经过我自己的实践,我决定写一个心得,给需要实现MD5,但又不要求很高深的编程知识的童鞋参考.不多说了 ...
- 2. EM算法-原理详解
1. EM算法-数学基础 2. EM算法-原理详解 3. EM算法-高斯混合模型GMM 4. EM算法-高斯混合模型GMM详细代码实现 5. EM算法-高斯混合模型GMM+Lasso 1. 前言 概率 ...
- 一致性算法RAFT详解
原帖地址:http://www.solinx.co/archives/415?utm_source=tuicool&utm_medium=referral一致性算法Raft详解背景 熟悉或了解 ...
随机推荐
- Python最强IDE(PyCharm)安装教程
欢迎关注公众号[Python开发实战],免费领取Python学习电子书! PyCharm是目前最流行.使用最广泛的Python IDE(Integrated Development Environme ...
- Spring按业务模块输出日志到不同的文件
一.背景 在我们开发的过程中,可能存在如下情况: 1.有些时候我们需要调用第三方的接口,一般情况下,调用接口,我们都会记录请求的入参和响应的.如果我们自己系统的日志和第三方的日志混合到一个日志文件中, ...
- 鸭的NOI ONLINE杂刷
好耶!洛谷账号橙了! 水题 [NOI Online #2 入门组] 未了 这就是一道贪心+二分查找,思路很好想 除法有精度问题,建议不使用除法 code [NOI Online #3 提高组] 水壶 ...
- linux项目部署(非前后端分离crm)
参考博客 参考博客2---部署过程 导论:看参考博客1 WSGI是Web服务器网关接口.它是一个规范,描述了Web服务器如何与Web应用程序通信,以及Web应用程序如何链接在一起以处理一个请求,(接收 ...
- 微信H5页面唤醒APP并传参跳转uniapp
主要实现是利用微信内置浏览器支持的<wx-open-launch-app>开放标签可以让你的H5网页拉起APP 在链接https://developers.weixin.qq.com/ ...
- K8S 使用Kubeadm搭建高可用Kubernetes(K8S)集群 - 证书有效期100年
1.概述 Kubenetes集群的控制平面节点(即Master节点)由数据库服务(Etcd)+其他组件服务(Apiserver.Controller-manager.Scheduler...)组成. ...
- Java基础(1)——ThreadLocal
1. Java基础(1)--ThreadLocal 1.1. ThreadLocal ThreadLocal是一个泛型类,当我们在一个类中声明一个字段:private ThreadLocal<F ...
- 两个月吃透阿里P9推荐260页SpringBoot2企业应用实战pdf入职定P6+
前言 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置 ...
- 腾讯云Redis全面升级,性能提升400%,可用性高达5个9
2022年6月,腾讯云Redis全新升级,发布高性能版本,单节点可提供50W+吞吐,性能是原生Redis的4倍.同时,腾讯云Redis推出全球复制功能,解决原生Redis诸多痛点问题,可用性升级高达9 ...
- CabloyJS 4.12震撼发布,及新版教程尝鲜
引言 凡是可以用 JavaScript 来写的应用,最终都会用 JavaScript 来写 | Atwood 定律 目前市面上出现的大多数与 NodeJS 相关的框架,基本都将 NodeJS 定位在工 ...