散列表 哈希表 原理 python实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/waltonhuang/article/details/52325416

参考

算法导论

引文

散列表(hash table)是实现字典操作的一种有效的数据结构。尽管最坏的情况下,散列表中查找一个元素的时间与链表中查找的时间相同,达到了O(n)。然而实际应用中,散列的查找的性能是极好的。在一些合理的假设下,在散列表中查找一个元素的平均时间是O(1)。

冲突和解决

利用哈希函数h(k),可以把关键字映射到一个小的数组中,但是会发生冲突。

解决方法有链接法和开放寻址法。

链接法

以链表的方式把冲突的元素往链表中插。

链表太长了性能下降如何优化?可以用完全散列结婚数,使用二次散列表。对冲突的元素再进行一次散列。

比如说,第一次算出来有好几个key的h(key)都等于2,h(key)=2,那就把这些冲突的元素再做一次散列。如果冲突的元素有n个,要求二次散列表的大小位m = n * n。

注:参考算法导论的举的例子(图11-6),从直接寻址表中引出的链表,还应存放二级散列表的哈希函数的参数。

算法导论:利用精心选择的散列函数hj,可以确保在第二级上不出现冲突。

开放寻址法

所有的元素都存放在散列表里。也就是说,每个表项或包含动态集合的一个元素,或包含NIL。当查找某个元素时,要系统地检查所有的表项,直到找到所需的元素,或者最终查明该元素不在表中。不像链表法,这里既没有链表,也没有元素存放在散列表外。因此在开放寻址法中,散列表可能会被填满,以至于不能插入任何新的元素。

发生冲突时有三种探查方式:

线性探查、二次探查、双重散列。

线性探查就是一直找下一个可以放的槽

h(k, i) = (h’(k) + i) mod m

二次探查是利用二次函数来查找下一个可以放的槽

h(k, i) = (h’(k) + c1*i + c2*i*i) mod m

双重散列的效果最好。

h(k, i) = (h1(k) + i * h2(k)) mod m

举个例子。

假设,散列表的大小为13,h1(k) = k mod 13,h2(k) = 1+ (k mod 11)。

第1次计算:h1(k) = 14 mod 13 = 1, 如果发现槽1被占,就继续算下一个。

h2(k) = 1 + (14 mod 11) = 4。

第1次冲突,要尝试的下一个槽的计算:

h(k, 1) = 1 + 1 * 4 = 5。尝试槽5。

如果槽5也被占了呢?第2次冲突,要尝试的下一个槽的计算:

h(k, 2) = 1 + 2 * 4 = 9。尝试槽9。

以此类推。

python是怎么实现dict的?

参考

Python字典实现

Python是使用开放寻址法中的二次探查来解决冲突的。然后如果使用的容量超过数组大小的2/3,就申请更大的容量。数组大小较小的时候resize为*4,较大的时候resize*2。实际上是用左移的形式。

《Python源码剖析》的笔记-第五章 Python中的dict对象

阅读更多
换一批

没有更多推荐了,返回首页