Python与数据结构[4] -> 散列表[1] -> 分离链接法的 Python 实现

分离链接法 / Separate Chain Hashing

 

前面完成了一个基本散列表的实现,但是还存在一个问题,当散列表插入元素冲突时,散列表将返回异常,这一问题的解决方式之一为使用链表进行元素的存储,即分离链接法。

Separate Chain Hashing:
        [0]  Header->11->0->110
        [1]  Header->12->1->111
        [2]  Header->2->112
        [3]  Header->14->3->113
        [4]  Header->15->4->114
        [5]  Header->16->5
        [6]  Header->17->6
        [7]  Header->18->7
        [8]  Header->19->8
        [9]  Header->9
        [10] Header->10

而在利用链表实现分离链接法时,可选用带表头的链表,插入元素时采用前端插入,每次将新元素插入对应散列位置链表的最前端,因为新插入的元素往往被查找的概率较大,放在前面便于缩短查找时间。

下面利用代码实现散列表的分离链接法,

完整代码

《Python与数据结构[4] -> 散列表[1] -> 分离链接法的 Python 实现》” /><br /> <img layer-src= 1 from hash_table import HashTable, kmt_hashing 2 from linked_list.linked_list_dummy_header import LinkedListDummyHeader as List 3 4 5 class SeparateChainHashing(HashTable): 6 """ 7 Separate Chain Hashing: 8 [0] Header->11->0->110 9 [1] Header->12->1->111 10 [2] Header->2->112 11 [3] Header->14->3->113 12 [4] Header->15->4->114 13 [5] Header->16->5 14 [6] Header->17->6 15 [7] Header->18->7 16 [8] Header->19->8 17 [9] Header->9 18 [10] Header->10 19 """ 20 def __init__(self, size, fn): 21 self._array = [List() for i in range(size)] 22 self._hashing = fn 23 24 def find(self, item): 25 linked_list = self._array[self._hashing(item)] 26 node = linked_list.header.next 27 while node and node.value != item: 28 node = node.next 29 return node 30 31 def _insert(self, item): 32 """ 33 item 34 | 35 V 36 [n] Header->node_1->node_2->node_3 37 """ 38 if item is None: 39 return 40 linked_list = self._array[self._hashing(item)] 41 node = linked_list.header 42 while node.next: 43 if node.next.value == item: # Element existed 44 return 45 node = node.next 46 linked_list.insert(item, 1) 47 48 def delete(self, item): 49 linked_list = self._array[self._hashing(item)] 50 linked_list.delete(item) 51 52 def show(self): 53 print(self) 54 55 @property 56 def load_factor(self): 57 element_num = sum(x.length-1 for x in self._array) 58 return element_num/self.size 59 60 def make_empty(self): 61 # self._array = [List() for i in range(len(self._array))] 62 for chain in self._array: 63 chain.clear() 64 65 66 def test(h): 67 print('\nShow hash table:') 68 h.insert(110, 111, 112, 113, 114) 69 h.insert(range(20)) 70 h.delete(13) 71 h.show() 72 print('\nLoad factor is:', h.load_factor) 73 print('\nClear hash table:') 74 h.make_empty() 75 h.show() 76 77 if __name__ == '__main__': 78 test(SeparateChainHashing(11, kmt_hashing(11)))

View Code

分段解释

首先导入散列表和散列函数,以及需要用到的带表头链表,

1 from hash_table import HashTable, kmt_hashing
2 from linked_list.linked_list_dummy_header import LinkedListDummyHeader as List

接着基于散列表派生一个实现分离链接法的散列表类,

 1 class SeparateChainHashing(HashTable):
 2     """
 3     Separate Chain Hashing:
 4         [0]  Header->11->0->110
 5         [1]  Header->12->1->111
 6         [2]  Header->2->112
 7         [3]  Header->14->3->113
 8         [4]  Header->15->4->114
 9         [5]  Header->16->5
10         [6]  Header->17->6
11         [7]  Header->18->7
12         [8]  Header->19->8
13         [9]  Header->9
14         [10] Header->10
15     """
16     def __init__(self, size, fn):
17         self._array = [List() for i in range(size)]
18         self._hashing = fn

重载find方法,在查找到散列值对应的链表后,遍历链表查询目标值,

1     def find(self, item):
2         linked_list = self._array[self._hashing(item)]
3         node = linked_list.header.next
4         while node and node.value != item:
5             node = node.next
6         return node

重载_insert方法,插入元素时,向链表表头后的第一个位置进行插入,

 1     def _insert(self, item):
 2         """
 3                    item
 4                     |
 5                     V
 6         [n]  Header->node_1->node_2->node_3
 7         """
 8         if item is None:
 9             return
10         linked_list = self._array[self._hashing(item)]
11         node = linked_list.header
12         while node.next:
13             if node.next.value == item:  # Element existed
14                 return
15             node = node.next
16         linked_list.insert(item, 1)

重载delete方法,删除元素较为简单,查找到散列值对应的链表后,使用链表的删除函数即可,

1     def delete(self, item):
2         linked_list = self._array[self._hashing(item)]
3         linked_list.delete(item)

最后,分别完成显示散列表,计算装填因子和清空散列表的函数,

 1     def show(self):
 2         print(self)
 3 
 4     @property
 5     def load_factor(self):
 6         element_num = sum(x.length-1 for x in self._array)
 7         return element_num/self.size
 8 
 9     def make_empty(self):
10         # self._array = [List() for i in range(len(self._array))]
11         for chain in self._array:
12             chain.clear()

完成散列表类后,再写一个测试函数,用于测试散列表,以 11 为散列表大小初始化一个散列表进行测试

 1 def test(h):
 2     print('\nShow hash table:')
 3     h.insert(110, 111, 112, 113, 114)
 4     h.insert(range(20))
 5     h.delete(13)
 6     h.show()
 7     print('\nLoad factor is:', h.load_factor)
 8     print('\nClear hash table:')
 9     h.make_empty()
10     h.show()
11 
12 if __name__ == '__main__':
13     test(SeparateChainHashing(11, kmt_hashing(11)))

最后得到结果

Show hash table:
[0] Header->11->0->110
[1] Header->12->1->111
[2] Header->2->112
[3] Header->14->3->113
[4] Header->15->4->114
[5] Header->16->5
[6] Header->17->6
[7] Header->18->7
[8] Header->19->8
[9] Header->9
[10] Header->10

Load factor is: 2.1818181818181817

Clear hash table:
[0] Header
[1] Header
[2] Header
[3] Header
[4] Header
[5] Header
[6] Header
[7] Header
[8] Header
[9] Header
[10] Header

可以看到,由于链表的存在,冲突被很好的解决了。而另一种冲突解决方式,可参考相关阅读中的开放定址法

 

相关阅读

1. 散列表

2. 开放定址法

 

    原文作者:StackLike
    原文地址: https://www.cnblogs.com/stacklike/p/8298410.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞