Python中哈希表/字典的实现

3
我一直在学习Python中的数据结构,并创建了一个简单的字典实现,如下所示。虽然这个实现最终是无用的(可以使用hash()而不是创建哈希函数等),但我对它们如何组合在一起感到很有兴趣。
这个实现选择了一个初始大小为11的值。self.capacity跟踪剩余的空闲插槽数量。添加(键,值)对时,它会减少1,一旦减少到0,每次需要新插槽时都会触发创建一个新插槽。
我的问题是:从哈希函数计算出的哈希值取决于len(self.slots),但是当我向字典添加更多空间时,此值会不断增加。我尝试使用初始大小(11)来计算哈希函数,但是一旦字典尝试添加第12个(键,值)对,程序似乎就会卡住。这似乎表明哈希函数需要基于表的大小,并且为了继续添加元素,我需要能够增加表的大小。这引出了以下问题:
  1. 字典是否必须初始化为固定长度并保持该长度?如果不是,则增加元素的首选方法是什么?
  2. 如果字典的长度可以更改,则哈希函数应该构造为不使用字典大小吗?如何确保值仍将减少到表插槽范围内的值,而不是通过模表大小进行减少?
非常感谢任何解释、有趣的见解或有用的提示。
class HashTable:
    def __init__(self):
        self.size = 11
        self.capacity = self.size
        self.slots = [None] * self.size
        self.data =  [None] * self.size

    def hashfunction(self, key, size):
        return key%size

    def rehash(self, oldhash, size):
        return (oldhash+1)%size

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

        if self.capacity < 1:
            self.slots += [None]
            self.data += [None]
            self.capacity += 1

        if self.slots[hashvalue] == None:
            self.slots[hashvalue] = key
            self.data[hashvalue] = value
            self.capacity -= 1
        else:
            if self.slots[hashvalue] == key:
                self.data[hashvalue] = data
            else:
                rehashed = self.rehash(hashvalue, len(self.slots))
                while self.slots[rehashed] != None and self.slots[rehashed] != key:
                    rehashed = self.rehash(rehashed, len(self.slots))
                if self.slots[rehashed] == None:
                    self.slots[rehashed] = key
                    self.data[rehashed] = value
                    self.capacity -= 1
                else:
                    self.data[rehashed] = value

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

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

        if self.slots[hashvalue] == key:
            self.slots[hashvalue] = None
            self.data[hashvalue] = None
        else:
            rehashed = self.hashfunction(hashvalue, len(self.slots))
            while self.slots[rehashed] != key:
                rehashed = self.hashfunction(rehashed, len(self.slots))
            if self.slots[rehashed] == key:
                self.slots[rehashed] == None
                self.data[rehashed] == None

    def __contains__(self, key):
        return key in self.slots

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

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

3
似乎这很适合http://codereview.stackexchange.com。 - Jean-François Fabre
1
也许吧,但我更感兴趣的是背后的理论,而不是如何改进我的代码的细节。 - Некто
此外,这不是有效的代码。它不应该出现在代码审查中。 - user2357112
无法正常工作的示例 - user2357112
1个回答

8

您需要将哈希和表格大小保持分开。哈希应仅基于键本身而不是大小。每个键值条目存储以下信息:

  • 哈希 - 从键派生的常数值。确保它具有广泛的扩展性以避免过多的冲突。

根据表格大小和哈希选择插槽:

slot = hash % tablesize

然后,当您的当前表中没有足够的空间时,生成一个表(比如说,将其大小加倍),以容纳不断增长的数据集,并重新分配所有内容。您已经缓存了哈希值,您所要做的就是取出每个(key, hash, value)元组,并使用上述公式重新计算新的插槽,现在是在更大的表格尺寸下。
您还需要决定如何处理冲突;即给定当前表大小,两个哈希值最终进入同一插槽的情况。Python的dict使用开放地址法,其中哈希值以可重复的方式“扰动”,直到找到另一个空插槽。
您可能想研究Python的dict源代码,以了解他们是如何处理这些问题的,可以查看关于如何处理冲突的详细注释。您也可以观看这个PyCon演讲,Brandon Rhodes在其中使用了一些非常启发性的图形解释了所有这些细节。或者您可以购买Beautiful Code,其中有一个完整的章节介绍了Pythondict的实现方式。

谢谢,这非常有帮助! - Некто

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接