数据结构与算法分析(五)--散列

本系列为《Python数据结构与算法分析》第二版学习笔记,作者:布拉德利.米勒;戴维.拉努姆。

1、散列

散列表是元素的集合,其中的元素以一种便于查找的方式存储。散列表中的每个位置通常被称为槽。

散列函数将散列表中的元素与其所属位置对应起来。槽的占用率被称作载荷因子,载荷因子 = 元素个数 / 散列表大小

在散列函数确定元素位置的时候,有时候它会将两个元素放入同一个槽,这种情况被称作冲突,也叫碰撞。

给定一个元素集合,能将每个元素映射到不同的槽,这种散列函数被称作完美散列函数。如果元素已知,并且集合不变,那么构造完美散列函数是可能的。不幸的是,给定任意一个元素集合,没有系统化方法来保证散列函数是完美的,所幸,不完美的散列函数也能有不错的性能。

通常来说,我们创建散列函数的目标是:冲突数最少,计算方便,元素均匀分布于散列表中。通常有以下几类方法:折叠法、平方取中法等。

2、处理冲突

当两个元素被分到同一个槽中时,必须通过一种系统化方法在散列表中安置第二个元素,这个过程被称为处理冲突。

一种方法是在散列表中找到另一个空槽,用于放置引起冲突的元素。简单的做法是从起初的散列值开始,顺序遍历散列表,直到找到一个空槽。为了遍历散列表可能需要往回检查第一个槽。这个过程被称为开放定址法或叫线性探测

线性探测有个缺点,那就是会使散列表中的元素出现聚集现象,也就是说,如果一个槽发生太多冲突,线性探测会填满其附近的槽,而这会影响到后后续插入的元素。

3、实现映射抽象数据类型

映射支持以下操作:

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

HashTable类的构造方法:

class HashTable:
    def __init__(self):
        self.size = 11
        self.slots = [None] * self.size
        self.data = [None] * self.size

使用两个列表创建HashTable类,以此实现映射抽象数据类型。其中名为slots的列表用于存储键,名为data的列表用于存储值。
两个列表中的值一一对应,列表大小尽量选用一个素数,这样可以提高冲突处理的效率。

put函数

    def put(self, key, data):
        hashvalue = self.hashfunction(key, len(self.slots))

        if self.slots[hashvalue] is None:
            self.slots[hashvalue] = key
            self.data[hashvalue] = data
        else:
            if self.slots[hashvalue] == key:
                self.data[hashvalue] = data  # 替换
            else:
                nextslot = self.rehash(hashvalue, len(self.slots))
                while self.slots[nextslot] is not None and self.slots[nextslot] is not key:
                    nextslot = self.rehash(nextslot, len(self.slots))

                if self.slots[nextslot] is None:
                    self.slots[nextslot] = key
                    self.data[nextslot] = data

                else:
                    self.data[nextslot] = data  # 替换
    
    def hashfunction(self, key, size):
        return key % size

    def rehash(self, oldhash, size):
        return (oldhash + 1) % size  # +1是为了防止超出范围

get函数

    def get(self, key):
        startslot = self.hashfunction(key, len(self.slots))
        data = None
        stop = False
        found = False
        position = startslot

        while self.slots[position] is not None and not found and not stop:
            if self.slots[position] is key:
                found = True
                data = self.data[position]
            else:
                position = self.rehash(position, len(self.slots))
                if position is startslot:
                    stop = True
        return data

    def __getitem__(self, key):
        return self.get(key)

    def __setitem__(self, key, data):
        self.put(key, data)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值