概述

  • 哈希表是一种可以满足快速查找数据结构,时间复杂度接近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 哈希表和算法思路详解的更多相关文章

  1. C#中哈希表(HashTable)的用法详解以及和Dictionary比较

    1.  哈希表(HashTable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似keyvalue的键值对, ...

  2. C#中哈希表(HashTable)的用法详解

    描述: 哈希表存放 key.values ,key值可以用于快速调取用,values 对应object类型,也就是说所有类型. 实例: 1.HashTable存放学生的成绩 Hashtable ht1 ...

  3. 转 C#中哈希表(HashTable)的用法详解

    看了一遍有关哈希表的文字,作者总结的真是不错 .收藏起来 1.  哈希表(HashTable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提 ...

  4. javascript常用经典算法实例详解

    javascript常用经典算法实例详解 这篇文章主要介绍了javascript常用算法,结合实例形式较为详细的分析总结了JavaScript中常见的各种排序算法以及堆.栈.链表等数据结构的相关实现与 ...

  5. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

  6. 各大公司广泛使用的在线学习算法FTRL详解

    各大公司广泛使用的在线学习算法FTRL详解 现在做在线学习和CTR常常会用到逻辑回归( Logistic Regression),而传统的批量(batch)算法无法有效地处理超大规模的数据集和在线数据 ...

  7. MD5算法步骤详解

    转自MD5算法步骤详解 之前要写一个MD5程序,但是从网络上看到的资料基本上一样,只是讲了一个大概.经过我自己的实践,我决定写一个心得,给需要实现MD5,但又不要求很高深的编程知识的童鞋参考.不多说了 ...

  8. 2. EM算法-原理详解

    1. EM算法-数学基础 2. EM算法-原理详解 3. EM算法-高斯混合模型GMM 4. EM算法-高斯混合模型GMM详细代码实现 5. EM算法-高斯混合模型GMM+Lasso 1. 前言 概率 ...

  9. 一致性算法RAFT详解

    原帖地址:http://www.solinx.co/archives/415?utm_source=tuicool&utm_medium=referral一致性算法Raft详解背景 熟悉或了解 ...

随机推荐

  1. 技术管理进阶——一线Leader与大Leader的差异是什么?

    原创不易,求分享.求一键三连 ​ 前几年,因为下属的一些骚操作,Leader对我产生了误会,这导致了一些"不信任",这种不信任在短时间会让人丧失安全感,很多人会因此患得患失. 这种 ...

  2. 攻防世界-MISC:wireshark-1

    这是攻防世界高手进阶区的第五题,题目如下: 点击下载附件一,得到一个压缩包,解压后得到一个流量包,用wireshark打开,分组字节流搜索字符串flag password的值即是flag,所以这道题的 ...

  3. NLP教程(5) - 语言模型、RNN、GRU与LSTM

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www.showmeai.tech/article-det ...

  4. windows 存储和切换 ip 配置

    我的虚拟机用的是桥接模式,在公司使用时设置的是静态 ip,但网段和家里面的不一样,就导致在公司和家里,我需要频繁修改 ipv4 的配置以适应不同的网络环境 Simple-IP-Config 工具解决了 ...

  5. 817. Linked List Components - LeetCode

    Question 817. Linked List Components Solution 题目大意:给一个链表和该链表元素组成的一个子数组,求子数组在链表中组成多少个片段,每个片段中可有多个连续的元 ...

  6. mysql配置与存储引擎与字段类型与约束条件

    目录 字符编码与配置文件 存储引擎 创建表的完整语法 字段类型 整型 浮点型 字符类型 数字的含义 枚举与集合 日期类型 约束条件 字符编码与配置文件 在MySQL5.X系列中,显示的字符编码有多种, ...

  7. 使用pdfcrack & crunch暴力破解PDF密码

    crunch是密码字典生成器,可以根据指定的字符来生成组合密码字典. pdfcrack是Linux下一个PDF暴力破解密工具,可以使用crunch生成的密码字典来暴力破解PDF文件的密码. 注:没有强 ...

  8. 动态调试JS脚本文件:(JS源映射 - sourceURL)与 debugger

    我们在进行js调试时经常会对js进行调试,chrome 对js提示对支持非常友好,只需要F12就可以打开chrome的调试器 在sources里面就是页面请求后加载的一些资源文件,我们可以找到我们的j ...

  9. 微信小程序开发 记录

    采坑了 微信小程序--TabBar不出现的一种原因 学习微信小程序中,遇到底部的TabBar不出现的问题.经过多番尝试,终于解决问题.在此记录问题产生的原因和对策.下面先描述错误现象,接着指出错误原因 ...

  10. Wget命令解释

    Wget主要用于下载文件,在安装软件时会经常用到,以下对wget做简单说明. 1.下载单个文件:wget http://www.baidu.com.命令会直接在当前目录下载一个index.html的文 ...