【算法笔记】4.2 散列(哈希)

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

1. 散列

(1)定义

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

(2)关键问题

  1. 散列函数H的设计:如何把原始数据集的每个元素映射到尽量互不相同的整数

    1. 直接定址法H(key) = a*key+b
    2. 数字分析法:适用于key是多位整数的情况,取key的若干数位组成哈希地址
    3. 平方取中法:关键字平方后取中间几位
    4. 折叠法:把关键字分割成位数相同的几段,取这几段的叠加和
  2. 冲突的解决方法:当两个元素数据映射到同一个整数时,如何解决冲突

    1. 线性探查法:若H(key)位置已被占据,就检查H(key)+1是否为空,如果也被占据就继续检查H(key)+2,这样不断向后探查直到找到一个空位置,如果后移到数组尾部,则返回到数组首部继续。这种方法保证只要哈希表未满,总能找到一个不冲突的地址,但是容易产生聚集影响效率
    2. 二次探测再散列:线性探查法是向一个方向找, 二次探测再散列是来回找,移动序列为 1 2 , − 1 2 , 2 2 , − 2 2 . . . 1^2,-1^2,2^2, -2^2... 121222,22...,这样不保证能找到不冲突地址,但不易发生聚集
    3. 伪随机探测再散列:和上两种类似,只是移动序列为随机序列
    4. 再哈希法:设定多个不同的散列函数,第一个函数计算发生冲突,就换第二个再算,直到不冲突为止
    5. 链地址法:这种方法不需要重新计算哈希值,而是把所有哈希值相同的key链接为一个单链表
  3. 如何查找

    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提供的工具。mapunordered_map(C++11)都可以用于散列

猜你喜欢

转载自blog.csdn.net/wxc971231/article/details/108441918
今日推荐