实现Hash表(四)----完成hash表中的删除操作

配合前置文章食用更佳,传送门如下:
实现Hash表(一)----手写一个简单hash表

实现Hash表(二)----处理index冲突问题

实现Hash表(三)----实现python中的字典

问题描述

目前增删改查我们已经完成了增、改、查,还差"删",实现是否是通过get_valid_index获取对应的index,之后将对应的数据变为None就行了呢?

问题分析

当然前面我都那么说了吗,答案肯定是no…

为什么呢?考虑如下几个问题:

1.删除了元素之后,假如只是将其置空,get_valid_index是否可以正常工作?
不可以,冲突时会影响后续的搜索,考虑序列A x B none x代表该元素被删除
当查找B的时候,首先假设映射到A,即A、B对应的hash值相同,向后查找,get_valid_index应该返回B对应的索引
但是这种情况下只会返回x对应的索引,找不到B对应的value
2.除此之外,删除后的位置应该可以进行复用,否则对空间会造成很大浪费

解决办法:

​ 引入一个标记----tombstone----这里我用(None,None)充当tombstone,用于标记已经删除的位置

但是随之而来的问题:效率下降
多次删除插入之后,你的搜索,可能需要遍历的长度变长
假设平均搜索的长度是1.2,也就是每次调用get_valid_index的时候,平均向后需要继续搜索0.2的距离,多次插入删除之后变为了1.6
,虽然看似变化不大,其实,其多出来的长度,变为了原来的三倍。
解决办法:
周期性删除tombstone,既可以删除tombstone,还可以趁这个机会,将经常搜索的key,变成get_valid_index可以直接获取的index,而不需要向后遍历,效率进一步提高。

python代码实现

  1. 可以和之前的代码对比,看看那些地方发生了改变

  2. 注意对none的判断,可以跑出异常,例如raise ValueError("查找的键值不能为none")

MAX_HASH_TABLE_SIZE = 4096


class DictByHashWithDel:
    def __init__(self, max_size=MAX_HASH_TABLE_SIZE):
        self.max_size = max_size
        self.data_list = [None] * max_size

    def get_valid_index(self, key):
        """
        :param key:
        :return: hash 返回一个hash值
        e.g hash('test') ---> 9223025588143776724
            hash(1) --->1
        """
        index = hash(key) % self.max_size
        while True:
            if self.data_list[index] is None:
                return index
            if self.data_list[index] == (None, None):
                return index
            key_value = self.data_list[index]
            if key_value[0] == key:
                return index
            index += 1
            if index == len(self.data_list):
                index = 0

    def delete(self, key):
        """
        :param key:
        :return:
        删除key所对应的元素
        """
        index = self.get_valid_index(key)
        self.data_list[index] = (None, None)

    def __getitem__(self, key):
        """
        :param key:  hash表中存储的key
        :return: 查找到的值
        """
        if key is None:
            raise ValueError("查找的键值不能为none")
            return
        return self.data_list[self.get_valid_index(key)][1]

    def __setitem__(self, key, value):
        """
        :param key:
        :param value:
        :return:
        实现更新/插入的逻辑
        """
        if key is None and value is None:
            raise ValueError("无效操作")
            return
        index = self.get_valid_index(key)
        self.data_list[index] = (key, value)

    def __iter__(self):
        return (x for x in self.data_list if x is not None and x != (None, None))

    def __len__(self):
        return len([x for x in self])

    def __repr__(self):
        """
        :return:print的时候会调用这个
        """
        from textwrap import indent
        pairs = [indent("{} : {}".format(repr(kv[0]), repr(kv[1])), '  ') for kv in self]
        return "{\n" + "{}".format(',\n'.join(pairs)) + "\n}"

    def __str__(self):
        return repr(self)


if __name__ == '__main__':
    # Create a hash table
    table = DictByHashWithDel()

    # Insert some key-value pairs
    table['a'] = 1
    table['b'] = 34

    # Retrieve the inserted values
    print(table['a'] == 1 and table['b'] == 34)

    # Update a value
    table['a'] = 99

    # Check the updated value
    print(table['a'] == 99)

    # Get a list of key-value pairs
    # 这里可能会不一样,如果取1024就是对的,默认值会得到[('b', 34), ('a', 99)]
    print(list(table) == [('a', 99), ('b', 34)])

    table.delete('a')
    print(table)
    print(len(table))
    for i in table:
        print(i)
    table['a'] = 11
    print(table)
    print(len(table))

总结

通过对上一篇文章的代码改进,实现了hash表中的删除操作,但是这个操作很繁琐,复杂,还有其他方式可以实现hash表吗?具体内容参见:

实现Hash表(五)----换一种方式实现hash表

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值