赞
踩
目录
为什么计算索引位置用hash&(数组长度-1),而不是hash%数组长度?
- # 哈希表链表实现:哈希表为顺序表,哈希码为链表
- # 节点类
- class Node:
- def __init__(self, key, value):
- self.key = key # 键
- self.value = value # 值
- self.next = None # next
-
-
- class Hash:
- def __init__(self, n: int, factor: float = 0.75):
- """
- :param n: 哈希表的初始大小,要求n为2的n次方
- :param factor: 哈希表的扩容因子
- """
- # 要求n为2的n次方
- while n % 2 != 0:
- n = int(input('n不是2的n次方,不满足要求,请重新设置哈希表的长度:n='))
- # 构造空的哈希表,列表的索引代表哈希码,初始键为-1,代表该哈希码下没有元素
- # 给每个哈希码链表构造一个哨兵节点
- self.table = []
- self.create(n)
- self.num = 0 # 哈希表的元素个数,静态变量
- self.factor = factor # 哈希表的扩容因子
-
- def create(self, n):
- """
- 哈希表扩容/创建哈希表,O(N)
- :param n: 哈希表大小/哈希表新增长度
- """
- for _ in range(n):
- node = Node(-1, -1)
- self.table.append(node)
-
- def getLength(self):
- # 获得哈希表的长度
- return len(self.table)
-
- def getExpandThread(self):
- # 获得哈希表的扩容阈值
- return self.factor * self.getLength()
-
- def getContractThread(self):
- # 获得哈希表的收缩阈值
- return (1-self.factor) * self.getLength()
-
- def getHashCode(self, key): # 此处为哈希算法
- # 哈希算法,将键映射为哈希码,O(1)
- length = self.getLength()
- # 按位与运算&获得哈希码,也就是哈希表的索引
- hashCode = key & (length-1) # 等价于key%length,但是&比%更高效
- return hashCode
-
- def expand(self):
- # 哈希表扩容,长度倍增,阈值改变,o(n*m)
- length = self.getLength()
- self.create(length) # O(N)
- print(f'------元素个数:{self.num}>=阈值:{length*self.factor},需要扩容:{length}->{self.getLength()}------')
- # 元素重分配,O(N*M)
- for t in range(length): # O(N)
- head = self.table[t]
- if head.next is None or head.next.next is None:
- continue
- print(f'哈希码链表元素重分配,hash={t}:')
- aHead = Node(-1, -1)
- bHead = Node(-1, -1)
- pa = aHead
- pb = bHead
- while head.next: # O(M)
- head = head.next
- flag = head.key & length
- if flag == 0:
- pa.next = head
- pa = head
- else:
- pb.next = head
- pb = head
- if aHead.next:
- pa.next = None
- self.table[t] = aHead
- self.travel(t)
- if bHead.next:
- pb.next = None
- self.table[t+length] = bHead
- self.travel(t+length)
-
- def contract(self):
- # 删除元素之后,判断是否收缩哈希表
- length = self.getLength()
- print(f'------元素个数:{self.num}<=阈值:{length * (1-self.factor)},需要收缩哈希表:{length}->{length/2}------')
- newLength = int(length/2)
- # 元素重分配
- for t in range(newLength): # O(N)
- if self.table[t+newLength].next:
- head = self.table[t]
- while head.next:
- head = head.next
- head.next = self.table[t+newLength].next
- self.table = self.table[:newLength]
-
- def travel(self, hashCode=-1):
- if hashCode == -1: # 遍历哈希表,O(NxM)
- print('----------travel table start------------')
- for i in range(self.getLength()): # O(N)
- head = self.table[i]
- if head.next:
- print('hash=', i)
- while head.next: # O(M)
- head = head.next
- print(head.key, head.value)
- print('----------travel table end------------')
- else:
- # 遍历哈希码链表,O(M)
- print(f'----------travel hash={hashCode} start------------')
- if hashCode < 0 or hashCode >= self.getLength():
- print('输入的哈希码错误,可输入范围应为:0-', self.getLength()-1)
- return
- head = self.table[hashCode]
- while head.next:
- head = head.next
- print(head.key, head.value)
- print(f'----------travel hash={hashCode} end------------')
-
- def remove(self, key):
- # 根据key值删除元素,并返回删除元素的value
- # 1、找key对应的哈希码
- hashCode = self.getHashCode(key)
- # 2、删除元素
- head = self.table[hashCode]
- while head.next and head.next.key != key:
- head = head.next
- # 退出循环时,如果key存在,则head指向待删除结点的前一个结点,如果key不存在,则head指向尾节点
- value = None
- if head.next: # 如果key存在,则head.next一定不为空
- value = head.next.value
- head.next = head.next.next
- print('remove:key存在,删除元素:', key, value)
- self.num -= 1
- if self.num <= self.getContractThread():
- self.contract()
- else:
- print('remove: key不存在,无需删除')
- return value
-
- def add(self, key, value):
- # 如果key不重复,则新增;如果key重复,则更新,O(m),m为哈希码链表的长度
- # 1、映射键为哈希码
- hashCode = self.getHashCode(key)
- # 2、添加到哈希码链表, O(m),m为哈希码链表的长度
- # 由于存在哨兵节点,所以插入第一个元素的操作也不用特殊判断
- cur = self.table[hashCode]
- while cur.next:
- cur = cur.next
- if cur.key == key: # 直接修改key对应的value,不作新增/扩容,因为key唯一
- print(f'add:key={key}重复,只更新值:{cur.value}->{value}')
- cur.value = value
- return
- # 新增一个元素
- # 如果正常退出循环,cur指向最后一个元素
- node = Node(key, value)
- cur.next = node
- print('add:key不重复,新增元素:', key, value)
- # 哈希表元素个数加1
- self.num += 1
- # 判断哈希表是否需要扩容
- if self.num >= self.getExpandThread():
- self.travel()
- self.expand()
-
- def getKey(self, key):
- # 查找某个键对应的值,O(m),m为哈希码链表长度
- # 1、找key对应的哈希码
- hashCode = self.getHashCode(key)
- # 2、查找
- cur = self.table[hashCode]
- while cur.next:
- cur = cur.next
- if cur.key == key:
- print(f'查找元素:key={key}对应的value={cur.value}')
- return cur.value
- print(f'查找元素:哈希表中不存在key={key}')
- return None
-
- def updateKey(self, key, value):
- # 更新key对应的值,O(M)
- hashCode = self.getHashCode(key)
- cur = self.table[hashCode]
- while cur.next:
- cur = cur.next
- if cur.key == key:
- print(f'更新元素:更新key={key}的value:{cur.value}->{value}')
- cur.value = value
- break
-
-
- table = Hash(8)
- table.add(0, 0)
- table.add(3, 3)
- table.add(4, 4)
- table.add(8, 8)
- table.travel()
- table.travel(3)
- table.getKey(3)
- table.add(3, 8)
- table.travel()
- table.getKey(3)
- table.updateKey(4, 8)
- table.travel()
- table.getKey(2)
- table.add(16, 16)
- table.add(24, 24)
- table.add(32, 32)
- table.add(40, 40)
- table.travel()
- table.remove(0)
- table.remove(3)
- table.remove(8)
- table.remove(32)
- table.travel()
'运行
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。