• 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('
    Show hash table:')
    68     h.insert(110, 111, 112, 113, 114)
    69     h.insert(range(20))
    70     h.delete(13)
    71     h.show()
    72     print('
    Load factor is:', h.load_factor)
    73     print('
    Clear 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('
    Show hash table:')
     3     h.insert(110, 111, 112, 113, 114)
     4     h.insert(range(20))
     5     h.delete(13)
     6     h.show()
     7     print('
    Load factor is:', h.load_factor)
     8     print('
    Clear 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. 开放定址法

  • 相关阅读:
    【.Net--资料】
    【系统Configmachine.config与自己的应用程序的App.config/Web.Config配置节点重复】解决方法
    【AutoMapper】实体类间自动实现映射关系,及其转换。
    【EntityFramwork--处理数据并发问题】
    【IOC--Common Service Locator】不依赖于某个具体的IoC
    Android学习——碎片Fragment的使用
    Android学习——利用RecyclerView编写聊天界面
    Android学习——控件ListView的使用
    Android学习——LinearLayout布局实现居中、左对齐、右对齐
    Android学习——Button填充颜色及实现圆角
  • 原文地址:https://www.cnblogs.com/stacklike/p/8298410.html
Copyright © 2020-2023  润新知