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
而在利用链表实现分离链接法时,可选用带表头的链表,插入元素时采用前端插入,每次将新元素插入对应散列位置链表的最前端,因为新插入的元素往往被查找的概率较大,放在前面便于缩短查找时间。
下面利用代码实现散列表的分离链接法,
完整代码
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)))
分段解释
首先导入散列表和散列函数,以及需要用到的带表头链表,
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. 开放定址法