目录
1.定义及本质
散列是常用的算法思想之一,散列的本质是一种数学映射,是输入(input)通过散列函数(hash function)映射到输出(output)的一种压缩变换。
百科上是这样介绍散列的:
关联数组是这样的数组,它的每个数据元素与一个键相对配对,该键用于识别数据元素。由于散列函数用来创建关联数组中的指定元素,并在关联数组中查找指定元素,因此关联数组通常称为散列。在某种意义上,数组元素与列表类似,而散列元素的存放与几何类似,其元素之间没有相对次序。在Ruby中,数组与散列之间的两个最本质的区别是:数组使用数值下标来定位特定的元素,而散列使用字符串值(键)来定位元素;数组中的元素按下标排序,而散列中的元素则不是。散列的创建方法有两种:new方法或将一个字面量赋值给一个变量。
准确地说,数组是数字下标与数值的映射,而散列则是任何类型的数字/字符/字符串都可以进行相关映射,来定位元素。无论从范围还是内涵上,散列都具有更一般的含义。
关于散列函数,定义如下:
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
2.基本概念
- 若结构中存在和关键字K相等的记录,则必定在f(K)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数(Hash function),按这个事先建立的表为散列表。
- 对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称碰撞。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“象” 作为记录在表中的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。
- 若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。
性质
所有散列函数都有如下一个基本特性:如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。这个特性是散列函数具有确定性的结果。但另一方面,散列函数的输入和输出不是一一对应的,如果两个散列值相同,两个输入值很可能是相同的,但不绝对肯定二者一定相等(可能出现哈希碰撞)。输入一些数据计算出散列值,然后部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。
典型的散列函数都有无限定义域,比如任意长度的字节字符串,和有限的值域,比如固定长度的比特串。在某些情况下,散列函数可以设计成具有相同大小的定义域和值域间的一一对应。一一对应的散列函数也称为排列。可逆性可以通过使用一系列的对于输入值的可逆“混合”运算而得到。
3.经典实例
先来看一个简单例子:给出N个正整数,再给出M个正整数,问这M个数中的每一个数分别是否在N个数中出现过。
最朴素的思路是将M中的每个元素在N个元素的数组中挨个查找,找到说明存在;找不到说明不存在。这种情况下的时间复杂度位O(N*M)。这种方法的弊端是如果数据量太大,比如达到10^5级别,时间效率就会变得极低。
还可以采用先将N个元素快速排序,在采用二分查找的方法,这种方法会提高效率。但是如果数据过多,快排和查找的时间就会比较长。这种情况下可以开辟一个数组。比如我们有一个字符串,就可以开辟一个a[128]的数组,数组初始化为0,然后查找,凡是出现过的字符,都记录为1,这样再判断下一个字符时,就可以根据是否为0就可以判断该字符之前是否出现过。
浏览网页时,键入的网址(WWW)会通过DNS解析:DNS resolution 转化成IP地址(比如:199.24.123.34)。DNS解析的过程就是散列函数的映射和变换。
3.1常用HASH函数
·直接取余法:f(x):= x mod maxM ; maxM一般是不太接近 2^t 的一个质数。
·乘法取整法:f(x):=trunc((x/maxX)*maxlongit) mod maxM,主要用于实数。
·平方取中法:f(x):=(x*x div 1000 ) mod 1000000); 平方后取中间的,每位包含信息比较多。
3.2构造方法
散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快地定位。
(详细构造方法可以参考hash函数中的【哈希表的构造方法】)
1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)
2. 数字分析法
3. 平方取中法
4. 折叠法
5. 随机数法
6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。