咱们都知道,二叉树、红黑树等数据结构,它们的查找都是先从根节点开始查找,从节点取出数据或者索引与查找值进行比较。数组
那么,有没有一种函数H,根据这个函数和查找关键字 Key,能够直接肯定查找值所在的位置,而不须要一个个比较。这样叫“预先知道” Key 所在的位置,直接找到数据,提高效率。即:数据结构
地址index = H(key)dom
说白了,hash函数功能就是根据 key 计算出应该存储地址的位置,而哈希表是基于哈希函数创建的一种查找表。函数
根据前人经验,统计出集中经常使用 hash 函数构造方法设计
哈希函数为关键字的线性函数,如 H(key) = a * key + b,这种构造方法比较简单,均匀。可是有很大限制,仅限与地址大小 = 关键字集合的状况。指针
假设关键字集合中的每一个关键字 key 都是由s位数字组成(k1,k2,……,kn),分析 key 中的全体数据,并从中提取分布均匀的若干位或他们的组合构成全体。code
使用举例:假设要保存的集合中 key 由 5 位不一样的数字构成。而且这些数字分布均匀,咱们能够提取其中一位,表明数据保存的地址。H(key) = key % 100000。索引
此种方法一般用于数字位数较长的状况,要求数字存在必定的规律,其次必须知道数字的分布状况。hash
若是关键字的每一位都有某些数字重复出现频率很高的现象,能够先求关键字的平方值,经过平方扩大差别,然后取中间数位做为最终存储地址。table
使用举例:
好比:key = 1234 1234^2 = 1522756 取 227 做hash地址
好比:key = 4321 4321^2 = 18671041 取 671 做hash地址
这种方法适合事先不知道数据而且数据长度较小的状况。
若是数字的位数不少,能够将数字分割为几个部分,取他们的叠加和做为hash地址。
使用举例:
好比 key=123 456 789
咱们能够取 1368(123+456+789=1368) 做为 hash 地址。
该方法适用于数字位数较多且事先不知道数据分布的状况。
H(key)=key MOD p (p<=m m为表长) 很明显,如何选取p是个关键问题。
使用举例
好比咱们存储3 6 9,那么p就不能取3
由于 3 MOD 3 == 6 MOD 3 == 9 MOD 3
p应为不大于m的质数或是不含20如下的质因子的合数,这样能够减小地址的重复(冲突)
好比key = 7,39,18,24,33,21 时取表长m为9 p为7 那么存储以下:
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
Key | 7 | 21(冲突后移) | 24 | 39 | 18(冲突后移) | 33(冲突后移) |
H(key)= random (key) 取关键字的随机函数为它们的散列地址。
上面已经提到,不一样 key 经过 hash 函数产生相同的地址,即 H(key1) == H (key2),此时,key1 和 key2 就发生了 hash 冲突。
无论 hash 函数设计的如何巧妙,总会有特殊的 key 致使 hash 冲突, 特别是对童泰查找表来讲。
首先有一个H(key)的哈希函数。
若是H(key1)= H(keyi),那么 keyi 存储位置 Hi = (H(key)+di) MOD m。其中 m 是表长。
di 有三种取法:
产生hash冲突后在存储数据后面加一个指针,指向后面冲突的数据。
创建一个特殊存储空间,专门存放冲突的数据。此种方法适用于数据和冲突较少的状况。
准备若干个hash函数,若是使用第一个hash函数发生了冲突,就使用第二个hash函数,第二个也冲突,使用第三个……
查找过程和造表过程一致,假设采用开放定址法处理冲突,则查找过程为:
对于给定的 key,计算 hash 地址 index = H(key)。
若是数组 arr[index] == null, 则查找不成功;
若是数组 arr[index] == key,则查找成功;
若是 (arr[index] != null && arr[index] != key),使用 hash 冲突解决方法求下一个地址,直到(arr[index] == null || arr[index] == key)。
决定 hash 表查找的 ASL 因素: