可扩展散列,将指针数组的大小加倍

Extendible Hashing, doubling the size of an array of pointers

本文关键字:数组 指针 可扩展      更新时间:2023-10-16

我正在尝试在c++中实现可扩展哈希

有一个结构体作为索引,它包含一个类型为'Bucket'的数组

Bucket * bucket_pointers;

还有一个结构体Bucket,它有一个数组,里面保存着我的值

E values[N] = {};

我有一个或多或少工作的程序,有一个问题:每次我要把我的哈希表的大小翻倍,我复制我所有的桶到一个新的数组(大小的两倍)

Index_0
Bucket <n= 3, local_depth=2, 0x100200000>
[12,4,,8,]
Index_1
Bucket <n= 0, local_depth=1, 0x100200028>
[,,,,]
Index_2
Bucket <n= 3, local_depth=2, 0x100200050>
[2,10,6,,]
Index_3
Bucket <n= 0, local_depth=1, 0x100200078>
[,,,,]

然而,地址为0x100200078的桶实际上应该指向地址为0x100200028的桶,即两个索引(1和3)应该指向同一个桶。

这里我决定是否分割桶或加倍我的索引的大小…

while (!bucket_pointers[h%index_size].append(e)){ 
    if(bucket_pointers[h%index_size].local_depth<global_depth){
        split(hashValue);
    }
    else if(bucket_pointers[h%index_size].local_depth==global_depth){
        resize();
    }
}

我现在将数组的大小翻倍,像这样:

for (size_t i = 0; i < index_size;  ++i){
            for (size_t j = 0; j < bucket_pointers[i].n;  ++j){ 
                newBucket_pointers[i] = bucket_pointers[i];
                newBucket_pointers[i+index_size] = bucket_pointers[i];
            }
    }

注意,Bucket * bucket_pointers;不是Bucket指针数组,因为它的名字暗示。这是一个指向Bucket(指定Bucket数组中的第一个Bucket)的指针。

因此,当您将桶数组复制到另一个桶时,您最终会得到每个桶具有自己的values数组的相同副本。

newBucket_pointers[i] = bucket_pointers[i];
newBucket_pointers[i+index_size] = bucket_pointers[i];

如果你想让newBucket_pointers[i]newBucket_pointers[i+index_size]是指向同一个Bucket的指针,那么bucket_pointers(和newBucket_pointers)的类型实际上应该是Bucket**。那么bucket_pointers是指向Bucket*的指针,bucket_pointers[i]是指向Bucket的指针。这样,bucket_pointers[i], newBucket_pointers[i]newBucket_pointers[i+index_size]将指向同一个Bucket。为了更容易管理内存,我推荐使用std::vector<Bucket*> bucket_pointers

相反,如果你打算像现在一样复制bucket,但是让它们的values成员指向一个共享数组,那么你可以保持bucket_pointers不变,你需要将values的类型更改为指针并单独分配数组。如果您想以这种方式共享数组,您可能应该使用shared_ptr,以使最终的释放更容易。

我在下面包含了一些代码,它们作为一个非常简单的哈希表执行。它仅用于教学目的,对于在实际应用程序中使用不够健壮。在实际生活中,使用内置的std::unordered_set,效果会好得多。

通过使用链表作为可以根据需要扩展的存储桶,我避免了更改存储桶大小的需要。

这个例子对你走上正轨有帮助吗?

#include <iostream>
#include <array>
#include <list>
#include <string>
#include <cassert>

class CTable
{
public:
    void Add(const std::string &sKey, int nVal);
    int  Find(const std::string &sKey);
protected:
    size_t Index(const std::string &sKey);
private:
    struct SData
    {
        SData(const std::string &s, int n)
        : sKey(s)
        , nVal(n)
        {
        }
        std::string sKey;
        int         nVal;
    };
    typedef std::list<SData> Bucket_t;
    enum { nBuckets = 24 };
    typedef std::array<Bucket_t, nBuckets> Table_t;
    Table_t m_table;
    const SData *Lookup(const Bucket_t &b, const std::string &sKey);
};
void CTable::Add(const std::string &sKey, int nVal)
{
    size_t nIndex = Index(sKey);
    const SData *p = Lookup(m_table.at(nIndex), sKey);
    if (p)
        throw std::runtime_error("duplicate key");
    m_table.at(nIndex).push_back(SData(sKey, nVal));
}
int CTable::Find(const std::string &sKey)
{
    size_t nIndex = Index(sKey);
    const SData *p = Lookup(m_table.at(nIndex), sKey);
    if (p)
        return p->nVal;
    else
        throw std::runtime_error("not found");
}
size_t CTable::Index(const std::string &sKey)
{
    return std::hash<std::string>()(sKey) % m_table.size();
}
const CTable::SData *CTable::Lookup(const CTable::Bucket_t &b, 
                                    const std::string &sKey)
{
    for (const SData &s : b)
        if (s.sKey == sKey)
            return &s;
    return nullptr;
}

int main() 
{
    CTable t;
    t.Add("one", 1);
    t.Add("two", 2);
    t.Add("three", 3);
    assert(2 == t.Find("two"));
    try
    {
        t.Find("four");
        assert(false);
    }
    catch (std::exception &)
    {
    }
    try
    {
        t.Add("two", 3);
        assert(false);
    }
    catch (std::exception &)
    {
    }
    return 0;
}

正如@user2079303已经指出的,您想要的是Bucket**的数组。

让我用一些图片来说明:

Extendible-hashing解释

有一件事要记住,以防Bucket** index = new Bucket*[<size_here>]混淆了你,假设您想制作一个简单的int -数组。你可以这样做:

int* nums = new int[5];

简单地想象减少右边的*-符号的数量这就是定义内容类型。你只需要存储到bucket的地址。因此,index包含一个或多个指向bucket的指针

希望有帮助!