算法笔记4.2 散列(哈希)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法笔记4.2 散列(哈希)相关的知识,希望对你有一定的参考价值。


  • 这是《算法笔记》的读书记录
  • 本文参考自4.2节

文章目录

  • ​​1. 散列​​
  • ​​(1)定义​​
  • ​​(2)关键问题​​
  • ​​2. 整数的散列​​
  • ​​3. 其他数据类型的散列​​
  • ​​4. 编程中的应用​​

1. 散列

(1)定义

  1. 散列(hash)是一个常用思想,很多程序中都有意无意地会用到。 一句话说,散列就是 “将一个元素通过一个函数转换为整数,使得这个整数可以尽量唯一地代表这个元素”,这个转换函数称为 “散列函数 H”
  2. 经过散列函数处理,我们就可以把元素本身作为一个key/索引/下标…,进而实现时间复杂度为O(1)的查询。所以散列本质上是用通过空间换时间,实现快速查询的一种算法

(2)关键问题

  1. 散列函数H的设计:如何把原始数据集的每个元素映射到尽量互不相同的整数
  1. 直接定址法:​​H(key) = a*key+b​
  2. 数字分析法:适用于key是多位整数的情况,取key的若干数位组成哈希地址
  3. 平方取中法:关键字平方后取中间几位
  4. 折叠法:把关键字分割成位数相同的几段,取这几段的叠加和
  1. 冲突的解决方法:当两个元素数据映射到同一个整数时,如何解决冲突
  1. 线性探查法:若​​H(key)​​​位置已被占据,就检查​​H(key)+1​​​是否为空,如果也被占据就继续检查​​H(key)+2​​,这样不断向后探查直到找到一个空位置,如果后移到数组尾部,则返回到数组首部继续。这种方法保证只要哈希表未满,总能找到一个不冲突的地址,但是容易产生聚集影响效率
  2. 二次探测再散列:线性探查法是向一个方向找, 二次探测再散列是来回找,移动序列为【算法笔记】4.2,这样不保证能找到不冲突地址,但不易发生聚集
  3. 伪随机探测再散列:和上两种类似,只是移动序列为随机序列
  4. 再哈希法:设定多个不同的散列函数,第一个函数计算发生冲突,就换第二个再算,直到不冲突为止
  5. 链地址法:这种方法不需要重新计算哈希值,而是把所有哈希值相同的key链接为一个单链表
  1. 如何查找
  1. 一般的方法是:仿照建表过程,先用key计算H(key)作为下标,访问哈希表元素比较key,若不等,说明建表时发生了冲突,用建表时使用的冲突解决方法依次向后找,直到key比较相等为止

2. 整数的散列

  • 整数本身就是散列的目标输出,因此散列函数可以简单地表示为​​H(key)=key​​(直接定址法)
  • 举例来说,我们现在给出两个整数集合A和B,分别有M和N个元素,要判断B中的每个数是否在A中出现过
  1. 常规思路:二重遍历,时间复杂度​​O(M*N)​
  2. 使用散列:开一个大数组,在输入A时,用A中整数元素作为数组下标,对数组做标记。判断时只要用B中整数元素作为数组下标查询数组即可,时间复杂度​​O(M+N)​
  • PAT乙级中 “翻转链表” 这个题的正解就运用了整数散列的思路,否则一定会超时:​​反转链表 (25)​​

3. 其他数据类型的散列

  • 上面第一部分的散列方法基本都是针对于整数key而言的,要应用到其他数据类型,可以先简单地把数据转换为整数,再用上述方法,或者转换为整数后直接作为key。要注意的是,散列函数和冲突解决方法的设计都会影响效率
  • 举例来说,要设计一个英文单词的哈希函数,可以把大写字母转换为​​0~25​​​,小写字母转换为​​26~51​​,这就相当于把一个英文单词转换为52进制数,直接转10进制就可以作为key了

4. 编程中的应用

  • 做编程题时往往不会自己写散列函数和冲突处理什么的,而是采用STL提供的工具。​​map​​​和​​unordered_map​​(C++11)都可以用于散列


以上是关于算法笔记4.2 散列(哈希)的主要内容,如果未能解决你的问题,请参考以下文章

散列——排解冲突 开放定址法(上)

哈希算法(一次探测,二次探测,哈希桶法)支持字典查询

C# 通俗说 哈希表

Hash冲突的解决方法

C++ 哈希

20172314 三种查找算法练习