自定义实现简单的散列表(Python)

Python中的散列表

  • Python内置的字典数据类型的实现就是散列表
  • 散列表也被称为字典或者是关联数组(associative array)。
  • 和“关联数组”这个名称的字面意思一样,散列表会像Python的字典一样,把键和值关联起来。
  • 标准的数组数据结构能够让我们根据数组里的位置来查找值,而关联数组能够让我们根据键来查找值。

散列表的目标

实现散列表的目标是能够提供高效的插入、删除以及搜索的方法;而且,我们希望每种方法的通用情况都是Θ (1)的效率。

简单的例子

在我们的例子里,我们需要把每个字母都映射到数组里的相应位置。这个映射的操作的术语是散列函数。从例子的条件可以知道,在数组大小为26的时候,一个简单的散列函数可以把字母A映射到位置0上,把字母Z映射到位置25上。下面的Python函数就是这种散列函数的实现

def hash_letter(c):
    return ord(c)-ord('A') #ord 返回ascii的那个数字

hash_letter函数用了Python的ord函数来把字母转换为ASCII码,然后再减去字母A的ASCII码,于是就得到了一个0~25的值。因此,我们可以把这个函数一个大小为26的数组或列表一起使用。

也就是说,散列函数会把键映射到存储键值的数组或列表里的相应位置。如果键没有出现在散列表里的话,我们就必须要用一个特殊值来表明这个位置还没有被使用;在Python里,如果None不是一个会被存在散列表里的有效值的话,我们就可以使用它来作为这个特殊值。我们不需要把键存在数组里去,这是因为键都可以被一一映射到数组或列表里的每个位置。下面是散列表的Python版本的完整实现(没有用Python内置的字典),这个散列表将只允许用大写字母来作为键,而且每种方法的运行时间都是Θ (1):

class HahLetter(object):
    def __init__(self):
        self.table=26*[None,]
    def __getitem__(self,key):
        assert 'A'<=key<='Z'
        pos=ord(key)-ord('A')
        if self.table[pos]==None:
            raise KeyError(key)
        else:
            return self.table[pos]
    def __setitem__(self,key,value):
        assert 'A'<=key<='Z'
        pos=ord(key)-ord('A')
        self.table[pos]=value
    def __delitem__(self,key):
        assert 'A'<=key<='Z'
        pos=ord(key)-ord('A')
        self.table[pos]=None

HashLetter类包含了一个包含26个元素的列表的实例变量。我们会在每个位置都存入值None来表示散列表在这个位置上不包含任何的值。

在把键值对添加到散列表里的过程中,我们会先通过使用散列函数来把字母键映射到列表里的相应位置,然后再将值存储在列表里的这个位置。散列表并不支持为同一个键存储多个值(尝试在同一个键存储第二个值会覆盖第一个值)。当我们尝试查找一个字母键的时候,我们仍然会使用散列函数来把字母键映射到Python列表里的相应位置。如果列表里这个位置的值是None的话,那么代表这个字母键并不存在于散列表里,而且这个时候代码会像Python内置的字典那样抛出KeyError异常。如果列表里相应位置的值不是None的话,那么这就表示它是字母键相关联的值,因此会返回这个值。

d=HahLetter()
d['a']=4

报错,因为不再assert声明的范围内

assert 'A'<=key<='Z'
AssertionError
d=HahLetter()
d['A']=4
d['B']=5
print(d['A']) #4
d=HahLetter()
print(d['C'])

报错,因为C不存在

Traceback (most recent call last):
  File "D:main.py", line 29, in <module>
    print(d['C'])
  File "D:/main.py", line 16, in __getitem__
    raise KeyError(key)
KeyError: 'C'

在我们事先已经知道键可能出现的集合的情况下,这个简单的例子里所使用的策略是可以正常工作的。

然而遗憾的是,通常来说,这个策略并不太好。而且,当键可能出现的集合很大,但是键都不会被用到的时候,这个策略也不能很好地工作。比如说,我们的可能键的集合是0~20亿的所有整数,那么我们的数组就需要非常地大,从而需要比普通计算机内存还要大的存储空间。如果键可能出现的集合是英语单词怎么办?我们应该使用什么散列函数以及我们的数组应该有多大呢?如果两个键散列到了数组里的相同位置,我们应该怎么办?这些都是我们将会在这一节的其余部分进行讨论的实现问题。

总结

  • 想办法把key映射到数组中的位置就可以自己实现简单的散列表了

参考

Python ord() 函数 | 菜鸟教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值