Python Hash Table 散列表

发布于 2022-02-09 13:31:06 字数 2802 浏览 1201 评论 0

用于存储 key 对应的 value,给定 key,能够在非常快速的时间内找到 value。

设计一个散列函数,计算出关键字 key 对应的函数值 hashcode,作为数据对象 value 的存储地址。对某个关键字进行查找时,通过散列函数得到地址(或者是array的索引),通过索引访问数组直接得到这个 key 对应的 value,实现 O(1) 的时间复杂度。需要解决哈希冲突(Collision)的问题:即两个关键字映射到同一地址。一种解决办法是在产生冲突的地方使用链表存储,会带来新的问题就是如何确定 key 对应的 value 是链表中的哪个,所以这种情况下链表中不仅要存储 value,也要存储 key,也就是需要有两个 field,既存储 key,也存储 value

如果同一个 key 要插入不同的 value,有几种解决方式:一种是新插入的总是覆盖之前的 value,一种是允许一个key存在多个value,查找时随机返回一个 value,或者使用 find_all 返回所有 value

填充因子:n/N,已填充(已有的 key 的数量)/总空间;当填充因子变大时,会失去常数时间的效率,所以需要 resize:遍历原来的哈希表,re-hash 所有的 key,再把 value 存到新的哈希表的对应位置。当填充因子变小时,也需要 resize 释放内存

散列方法的存储对关键字是随机的,不适用于顺序查找关键字,不适用于最大/最小值查找或范围查找

散列函数的设计

散列函数最好计算高效,且映射之后对应的地址空间最好分布均匀以减少冲突。举例:

  • 数字:取模;平方取中法;折叠法(把数字拆分再相加)
  • 一个比较好的方法:hash_code(key) = (((a*key + b) mod p) mod N);p是一个远大于N 的素数(促进均匀分布),N是存储空间的长度(数组的长度)
  • 字符:ASCII 码加和再取模(很多冲突)
  • 一个比较好的方法(最终的 hashcode 取决于每个字符):
def hash_code(string_a, N):
  hash_val = 0
  for i in range(len(string_a)):
    hash_val = (127 * hash_val + string_a[i]) % 16908799
  return hash_val % N

哈希冲突

上面已经说了什么是哈希冲突,这里说一下处理哈希冲突的常用方法:

  • 线性探测法:发生冲突后,向前寻找一个空位来存储。注意删除操作应当将右侧所有相邻的键值对重新插入散列表中
  • 拉链法:在发生冲突的位置使用链表存储

这两种方法存储的时候都要同时存储 key 和 value,这样查找的时候才知道是否命中对应的key

主要功能实现:

使用拉链法解决哈希冲突;假设 key 是数字;如果同一个 key 插入不同的 value,则总是覆盖。

class listNode:
  def __init__(self, key, value):
    self.key = key
    self.value = value
    self.next = None

class HashTable:
  def __init__(self,N):
    self.size = N
    self.table = [None] * N

hash_code(key) -- 计算 key 的哈希值:参考上面提到的对应数字和字符串的散列方法

insert(key, value) -- 在哈希表中插入新的 key 及其对应的 value

def insert(self, key, value):
  index = self.hash_code(key)
  head = self.table[index]
  if not head:  # 如果哈希表对应位置还是空的
    self.table[index] = listNode(key, value)
  else:
    while head.next:
      head = head.next
    head.next = listNode(key, value)

find(key) -- 寻找 key 对应的 value

def find(self, key):
  index = self.hash_code(key)
  head = self.table[index]
  if not head:
    return None
  else:
    while head:
      if head.key == key:
        return head.value
      head = head.next
    return None

remove(key) -- 从哈希表中删除一个 key 及其对应的 value

def remove(self, key):
  index = self.hash_code(key)
  head = self.table[index]
  if not head:
    return None
  else:
    prev = None
    while head:
      if head.key == key:
        next_node = head.next
        if prev:
          prev.next = next_node
          return head.value
        else:
          self.table[index] = head.next
          return head.value
      else:
        prev = head
        head = head.next
    return None

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文