基本概念
散列表(Hash Table, 也叫哈希表),是根据关键码值(Key Value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找速度。这个映射函数叫做散列函数,存放记录的表叫散列表。
特点
- 若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。
- 对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2).这种现象称为碰撞。
- 若对于关键字集合中的任一个关键字,经散列函数映射到地址集合中的任何一个地址的概率是相等的,这种散列函数称为均匀散列函数。
一般的线性表、树中,记录在结构中的相对位置是随机的即和记录的关键字之间不存在确定的关系,在结构中查找记录时需进行一系列和关键字的比较。这一类查找方法建立在“比较”的基础上,查找的效率与比较次数密切相关。而哈希表的查找是根据哈希算法直接定位到对应的地址,与通过比较查找的结构相比。速度会有很大的提升。
存储实现
拉链法存储结构是一个经典的哈希表结构,可以理解为“链表的数组”。如图:
左边的数组用来存储一个链表指针,可以称之为桶(bucket)。而数组下标即经过哈希函数f(k)计算出的地址。
右边的链表是用来存储真正数据的,可以称之为槽(slot)。很显然,如果关键字(Key)不同的元素如果等到的地址相同,则这些元素会放在同一个链表里面。
哈希函数
常用的哈希函数:
1. 除法散列法
index = value % 16
2. 平方散列法
求index是非常频繁的操作,而乘法的运算要比除法来得省时(对现在的CPU来说,估计我们感觉不出来),所以我们考虑把除法换成乘法和一个位移操作。公式:
index = (value * value) >> 28 (右移,除以2^28。记法:左移变大,是乘。右移变小,是除。)
如果数值分配比较均匀的话这种方法能得到不错的结果,但我上面画的那个图的各个元素的值算出来的index都是0——非常失败。也许你还有个问题,value如果很大,value * value不会溢出吗?答案是会的,但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果,而是为了获取index。
3. 菲薄娜列(Fibonacci)散列法
平方散列法的缺点是显而易见的,所以我们能不能找出一个理想的乘数,而不是拿value本身当作乘数呢?答案是肯定的。
1,对于16位整数而言,这个乘数是40503
2,对于32位整数而言,这个乘数是2654435769
3,对于64位整数而言,这个乘数是11400714819323198485
这几个“理想乘数”是如何得出来的呢?这跟一个法则有关,叫黄金分割法则,而描述黄金分割法则的最经典表达式无疑就是著名的斐波那契数列,即如此形式的序列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,…。另外,斐波那契数列的值和太阳系八大行星的轨道半径的比例出奇吻合。
对我们常见的32位整数而言,公式:
index = (value * 2654435769) >> 28
转自(http://blog.csdn.net/v_july_v/article/details/6256463)
参考博客
http://blog.csdn.net/haoel/article/details/2863
http://spaces.isu.edu.tw/upload/18833/3/web/search.htm#_Toc231546394