哈希表是一个哈希函数来计算数据储存位置的数据结构,通常支持如下操作:
insert(key,value):插入健值对(key,value)
get(key):如果存在键key的健值则返回value,否则返回空值
delete(key):删除键为key的健值对
直接寻址表
直接寻址表可以理解为普通的字典,就是在整个域(内存)中去寻找健值(key)的位置,找到健值后,再通过健值去找到对应的内容。当关键字的全域比较小的时候,直接寻址是一种简单有效的方法。
但是当全域很打的时候,需要消耗大量的内存;且如果全域很大,但是实际出现的key很少,则会浪费大量空间;并且无法处理关键字不是数字的情况。
哈希(Hashing)
直接寻址表:将key=k的元素放在k上;
改进方法:哈希
1、构建好大小为m的寻址表
2、key=k的元素放在h(k)位置
3、h(k)是一个函数,主要是起到映射的作用
哈希表(散列表)是一种线性表的储存结构,是由 直接寻址表+哈希函数 组成,哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标。
哈希冲突是由于哈希表的大小是有限的,而存储的值的总数量是无限的,因此会出现两个不同元素映射到同一个位置上的情况,这种情况就叫做哈喜冲突。例如:h(k)=k%4,h(0)=h(4)=h(8)=……
解决哈希冲突的方法有很多种:开放寻址法;线性探查;二次探查;二度哈希;拉链法……
这里我们详细说明拉链法。拉链法是哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。
常见的哈希函数有:除法哈希法:h(k) = k % m;乘法哈希法:h(k) = floor(m*(Akey%1));全域哈希法:h-a,b-(k)=((akey+b) mod p) mod m a,b=1,2…,…p-1
我们需要先建立一个满足哈希表需求的链表,即将列表给转化为链表。
代码如下:
class LinkList():
class Node(): # 普通链表节点
def __init__(self,item):
self.item = item
self.next = None
class LinkListIterator(): # 创建链表迭代器
def __init__(self,node):
self.node = node
def __next__(self):
if self.node:
cur_node = self.node
self.node = cur_node.next
return cur_node.item # 返回得到另外一个节点
else:
raise StopIteration
def __iter__(self): # 这个必须要写,不写的话,显示LinkListIterator实例是不可迭代的 不懂看这个网站https://zhuanlan.zhihu.com/p/354115606
return self # return self 的作用是实例化对象 不懂可以看这个网站:https://blog.csdn.net/weixin_44799217/article/details/126131716
def __init__(self,iterable=None): # 没有输入的时候,就不需要扩展链表
self.head = None
self.tail = None
if iterable:
self.extend(iterable)
def append(self,obj): # 把列表元素转化为链表节点元素
s = LinkList.Node(obj)
if not self.head:
self.head = s
self.tail = s
else:
self.tail.next = s
self.tail = s
def extend(self,iterable): # 将列表元素一个一个给使用append函数转化为链表节点
for obj in iterable:
self.append(obj)
def find(self,obj): # 这里的self表示的是单个链表的所有内容
print(self)
for n in self:
if n == obj:
return True
else:
return False
def __iter__(self):
return self.LinkListIterator(self.head) # 允许链表迭代器从头节点开始迭代
def __repr__(self):
return "<<"+",".join(map(str,self)) + ">>" # 直接输出实例对象,而不是一个地址 不懂就看https://zhuanlan.zhihu.com/p/393657724
# 测试主函数
lk = LinkList([1,2,3,4,5])
print(lk)
# 建立哈希表的类(再上面链表的基础上)
# 类似于集合的结构
class HashTable():
def __init__(self,size=100):
self.size = size
self.T = [LinkList() for i in range(self.size)] # 创建一个列表,列表里面每一个元素都是链表
def h(self,k): # 建立返回余数的函数
return k % self.size
def insert(self,k):
i = self.h(k)
if self.find(k): # 起到去重的作用,已经存在的话就不再存进去了
print("Duplicated Insert.")
else:
self.T[i].append(k)
def find(self,k): # 返回链表设置的另外一个find函数,如果这个输入值在我们的链表里面了,就不用再插入了
i = self.h(k)
return self.T[i].find(k) # 在第i个位置去找里面是否有重复的数字
# 哈希表主函数
ht = HashTable()
ht.insert(0)
ht.insert(1)
ht.insert(0)
ht.insert(2)
ht.insert(101)
print(",".join(map(str,ht.T)))