hashtable :如何理解这种基于STL中List的hashtable实现

hashtable: how to understand this implementation of hashtable based on List in STL

本文关键字:hashtable STL List 实现 何理解      更新时间:2023-10-16

我正在学习用c ++构建一个哈希表。并找到这篇文章:https://www.geeksforgeeks.org/c-program-hashing-chaining/。

它通过链接实现了简单而基本版本的哈希表(不是生产级别(以修复哈希冲突问题。

我关注了该帖子并在本地运行它,它按预期工作。实现如下:

#include <iostream>
#include <list>
using namespace std;
class Hash {
int BUCKET;
list<int> *table; // confusing point1 
public:
Hash(int V);
void insertItem(int key);
void deleteItem(int key);
int hashFunction(int x) {
return (x % BUCKET);
}
void displayHash();
};
Hash::Hash(int b) {
this->BUCKET = b;
table = new list<int>[BUCKET]; // confusing point2
}
void Hash::insertItem(int key) {
int index = hashFunction(key);
table[index].push_back(key);
}
void Hash::deleteItem(int key) {
int index = hashFunction(key);
list <int> :: iterator i;
for (i = table[index].begin(); i != table[index].end(); i++) {
if (*i ==  key) {
break;
}
}
if (i != table[index].end()) {
table[index].erase(i);
}
}
void Hash:: displayHash() {
for (int i = 0; i < BUCKET; i++) {
cout << i;
for (auto x : table[i]) {
cout << "-->" << x;
}
cout << endl;
}
}
// Driver program  
int main() 
{ 
// array that contains keys to be mapped 
int a[] = {15, 11, 27, 8, 12}; 
int n = sizeof(a)/sizeof(a[0]); 
// insert the keys into the hash table 
Hash h(7);   // 7 is count of buckets in 
// hash table 
for (int i = 0; i < n; i++)  
h.insertItem(a[i]);   
// delete 12 from hash table 
h.deleteItem(12); 
// display the Hash table 
h.displayHash(); 
return 0; 
}  

关于此实现,我有两个令人困惑的点:

  • list<int> *table:表应该是存储桶数组。右?list<int> *应该是列表类型指针,对吧?它在这里是如何工作的?

  • table = new list<int>[BUCKET]: 我检查了许多列表相关
    文档。 但没有找到[]的工作原理?

list<int> *table: table 应该是存储桶数组。右?list<int>*应该是列表类型指针,对吧?它在这里是如何工作的?

在这个糟糕的代码中,table是指向list<int>的指针,但是当你有一个指向一个项目的指针并且碰巧知道那里有一个连续元素数组时,你可以像数组一样索引它,所以table[0]*table相同,table[1]将是内存中下一个list<int>,在table[0]之后,依此类推。

table = new list<int>[BUCKET]: 我检查了许多列表相关文档。 但没有找到 [] 的工作原理?

这是创建list<int>对象数组并将其地址保存在table中的初始化,因此我们确实"碰巧知道"数组存在并且可以table作为数组进行索引。 例如,在displayHash函数中,您会看到for (auto x : table[i])- 这意味着x从存储桶ilist<int>获取每个值,这是table[i]

代码还需要一个析构函数来delete[] table,否则当Hash对象的默认析构函数运行时,所有内存都将泄漏而不进行任何清理。

您还应该知道,它允许您插入同一密钥的多个副本 - 此功能的正确和完整实现是std::unordered_multiset

最低限度地清理它 - 无需采取后续步骤来使用模板以允许您将其用于其他类型的,添加迭代器等:

class Hash {
vector<list<int>> table_;
public:
Hash(size_t size) : table_{size} { }
void insert(int key) {
table_[bucket(key)].push_back(key);
}
void erase(int key) {
auto& bucket_list = table_[bucket(key)];
auto it = find(bucket_list.begin(), bucket_list.end(), key);
if (it != bucket_list.end())
bucket_list.erase(it);
}
int bucket(int key) const {
return hash(key) % table_.size();
}
static int hash(int key) {
return key;
}
// example usage: my_hash.display(std::cout);
void display(std::ostream& os) const {
for (size_t i = 0; i < table_.size_; ++i) {
os << '[' << i << ']';
for (auto x : table[i])
os << "-->" << x;
os << 'n';
}
}
// extensions ===================================
bool contains(int key) const {
auto& bucket_list = table_[bucket(key)];
auto it = find(bucket_list.begin(), bucket_list.end(), key);
return it != bucket_list.end();
}
// example usage:
//   my_hash.visit([](auto key) { std::cout << key << 'n'; });
template <typename Functor)
void visit(Functor functor) const {
for (size_t i = 0; i < table_.size_; ++i)
for (auto x : table[i])
functor(x);
}
};