“映射”抽象数据类型:ADT Map的python实现与散列冲突简介

python最有用的数据类型之一为“字典”,其是一种可以保存key-data键值对的数据类型。这种键值关联的无序集合称为“映射Map”
映射特点:(1)关键码具有唯一性; (2) 通过关键码可以唯一确定一个数据值。
映射支持以下操作:

  • Map()创建一个空的映射,它返回一个空的映射集合。
  • put(key, val)往映射中加入一个新的键-值对。如果键已经存在,就用新值替换旧值。
  • get(key)返回key对应的值。如果可以不存在,则返回None。
  • del 通过del map[key]这样的语句从映射中删除键-值对。
  • len()返回映射中存储的键-值对的数目。
  • in通过key in map这样的语句,在键存在时返回True,否则返回False。

注意:字典的一大优势是,给定一个键能很快地找到其关联的值。为了提高搜索效率,可以使用散列搜索算法,其时间复杂度可以达到O(1)。

映射的python实现:

##  映射数据类型的实现
# 使用两个列表创建Hashtable类
class HashTable:
    def __init__(self):# HashTable类的构造方法
        self.size = 11# 散列表的初始大小设置为素数,可以尽可能提高冲突处理算法的效率
        self.slots = [None] * self.size# 设置一个名为slots的列表,用来存储键,并初始化为None
        self.data = [None] * self.size# 设置一个名为data的列表,用来存储值,并初始化为None 
#实现一个名为hashfunction的散列函数,要存储或查找的键对散列表的大小进行取余运算。
    def hashfunction(self, key, size):
        return key % size
    def rehash(self, oldhash, size):
        return (oldhash+1) % size# oldhash为hashfunction()返回的值,但由于该位置上已经有了键,所以发生了冲突,需要使用rehash确定下一个位置
#3.实现put函数,首先使用hashfunction函数计算初始的散列值,
# 如果对应的位置上已有元素,就循环运行rehash函数,直到遇见一个空槽。如果槽中已有这个键,就用新值替换旧值。
    def put(self, key, data):
        hashvalue = self.hashfunction(key, len(self.slots))# 计算散列值,即key所对应的槽位
        if self.slots[hashvalue] == None:# 如果该槽位为空,则在该位置添加键-值对
            self.slots[hashvalue] = key
            self.data[hashvalue] = data
        else:
            if self.slots[hashvalue] == key:  # 如果键已经存在
                self.data[hashvalue] = data  # 用新值替换旧值
            else:  # 该槽位被其他键占用了,计算下一个散列值nextslot
                nextslot = self.rehash(hashvalue, len(self.slots))
                # 循环运行rehash直到遇见一个空槽,或发现键已经存在
                while self.slots[nextslot] != None and \
                        self.slots[nextslot] != key:
                    nextslot = self.rehash(nextslot, len(self.slots))
                if self.slots[nextslot] == None:  # 找到一个空槽位
                    self.slots[nextslot] = key
                    self.data[nextslot] = data
                else:  # 键已经存在
                    self.data[nextslot] = data
# 实现get函数,先计算初始散列值,如果值不在初始散列值对应的槽中,
# 就是用rehash确定下一个位置。如果遇到初始槽,就说明已经检查完所有可能的槽,并且元素必定不存在。
    def get(self, key):
        startslot = self.hashfunction(key, len(self.slots))
        data = None
        stop = False# 一旦遇到初始槽,则赋值为True,停止搜索
        found = False
        position = startslot、
        # 如果一开始初始槽就为空,则说明元素必定不存在
        while self.slots[position] != None and \
                not found and not stop:
            if self.slots[position] == key:# 如果找到key
                found = True
                data = self.data[position]# 取出key所对应的值
            else:# 如果上一个position已经被其他键占用,则计算下一个位置
                position = self.rehash(position, len(self.slots))
                if position == startslot:# 如果遇到初始槽,则搜索失败,元素不存在
                    stop = True# 停止搜索
        return data# 返回data,如果搜素失败,则返回None

    # 重载__getitem__和__setitem__, 以通过[]进行访问
    def __getitem__(self, key):
        return self.get(key)

    def __setitem__(self, key, data):
        self.put(key, data)

##  测试
H = HashTable()
H[54] = 'cat'
H[26] = 'dog'
H[93] = 'lion'
H[17] = 'tiger'
H[77] = 'bird'
H[31] = 'cow'
H[44] = 'goat'
H[55] = 'pig'
H[20] = 'chicken'
print(H.slots)
print(H.data)
print(H[20])
print(H[17])
H[20] = 'duck'
print(H.data)
print(H[99])
##测试结果:
[77, 44, 55, 20, 26, 93, 17, None, None, 31, 54]
['bird', 'goat', 'pig', 'chicken', 'dog', 'lion', 'tiger', None, None, 'cow', 'cat']
chicken
tiger
['bird', 'goat', 'pig', 'duck', 'dog', 'lion', 'tiger', None, None, 'cow', 'cat']
None

算法分析:
散列在最好的情况下,可以提供常数级别O(1)的时间复杂度的查找性能。但可能存在散列冲突,查找次数可能就没那么简单。
评估算列冲突的重要信息就是负载因子λ,其值较小,冲突的几率就小,较大是散列表填充较满,冲突就会越来越多。

关于散列冲突的计算,较为复杂,同时也是一个研究课题。限于篇幅,此处不做过多介绍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨猪起飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值