初始化哈希表C++中构造函数中的节点值
我应该创建一个散列映射来存储用户名和密码,它们存储为字符串。此哈希映射是单独链接的。当添加一个值或搜索一个值时,我应该在一个键上使用一个哈希函数,该键返回一个整数,该整数将为我需要使用函数的bucket提供索引。如果添加内容,我会将其存储在列表的末尾 所以我脑海中的散列图是这样的:初始化哈希表C++中构造函数中的节点值,c++,constructor,linked-list,hashmap,valgrind,C++,Constructor,Linked List,Hashmap,Valgrind,我应该创建一个散列映射来存储用户名和密码,它们存储为字符串。此哈希映射是单独链接的。当添加一个值或搜索一个值时,我应该在一个键上使用一个哈希函数,该键返回一个整数,该整数将为我需要使用函数的bucket提供索引。如果添加内容,我会将其存储在列表的末尾 所以我脑海中的散列图是这样的: Bucket | List [0] -> nullptr [1] -> (key1/value1) -> (key2/value2) [2] -> (key3/value3) struct
Bucket | List
[0] -> nullptr
[1] -> (key1/value1) -> (key2/value2)
[2] -> (key3/value3)
struct Node
{
std::string key;
std::string value;
Node* next;
};
从技术上讲,每个键/值组合都是一个节点,在我的头文件中具有不可修改的结构,如下所示:
Bucket | List
[0] -> nullptr
[1] -> (key1/value1) -> (key2/value2)
[2] -> (key3/value3)
struct Node
{
std::string key;
std::string value;
Node* next;
};
虽然我的add和search函数在技术上可以工作,但我希望在构造函数中正确初始化哈希映射,这样我就不会对未初始化的值进行比较。我的构造函数现在就是这样:
HashMap::HashMap()
: hashTable{new Node*[initialBucketCount]}, amountOfBuckets{initialBucketCount}, sz{0}
{
for(int i = 0; i < amountOfBuckets; i++) {
hashTable[i] = nullptr;
}
}
将表示ifcurrent==nullptr正在引用未初始化的值
如何正确初始化hashmap构造函数中的值?特别是如果我想在函数中执行某些操作,将列表的开头与nullptr这样的值进行比较,以便确定需要在列表中放置下一个节点的位置
编辑:这里是所有压缩成一个文件
/* HashMap.cpp */
namespace {
unsigned int hashFunc(const std::string& key)
{
unsigned int hashValue = 0; // what we end up returning
for(int i = 0; i < key.length(); i++) { // iterate through string
int letterIndex = key.at(i) - 96; // create an index for each ASCII char
hashValue = hashValue * 27 + letterIndex;
}
return hashValue;
} // end hashFunc
}
class HashMap
{
public:
typedef std::function<unsigned int(const std::string&)> HashFunction;
static constexpr unsigned int initialBucketCount = 10;
private:
struct Node
{
std::string key;
std::string value;
Node* next;
};
HashFunction hasher;
Node** hashTable;
unsigned int amountOfBuckets;
unsigned int sz;
public:
HashMap()
: hashTable{new Node*[initialBucketCount]}, amountOfBuckets{initialBucketCount}, sz{0}
{
for(int i = 0; i < amountOfBuckets; i++) {
hashTable[i] = nullptr;
}
}
HashMap(HashFunction hasher)
: hasher{hashFunc}, hashTable{new Node*[initialBucketCount]()}, amountOfBuckets{initialBucketCount}, sz{0}
{
for(int i = 0; i < amountOfBuckets; i++) {
hashTable[i] = nullptr;
}
}
HashMap(const HashMap& hm)
: hashTable{new Node*[hm.amountOfBuckets]}, amountOfBuckets{hm.amountOfBuckets}, sz{hm.sz}
{
for(unsigned int i = 0; i < sz; i++) {
hashTable[i] = hm.hashTable[i];
} // end for
}
~HashMap()
{
for(unsigned int i = 0; i < amountOfBuckets; i++) {
Node* bucketNode = hashTable[i]; // store each hashtable list in a bucket node
while(bucketNode != nullptr) {
Node* deleteCurrent = bucketNode; // set current to the bucket node (head)
bucketNode = bucketNode->next; // delete current is on the first node, update head to second node
delete deleteCurrent;
} // end while
} // end for
// once done, delete hash table
delete[] hashTable;
} // end destructor
void add(const std::string& key, const std::string& value)
{
unsigned int hashedValue = hashFunc(key); // get hash value (unmodified by buckets)
unsigned int tableIndex = getTableIndex(hashedValue); // get the table index
if(contains(key) == true) { // if key is already in the hashtable
return; // exit program
} else { // otherwise, key is not in the hash table
Node* head = hashTable[tableIndex]; // head of the hash table, represents
Node* current = head; // set current to first node
if(current == nullptr) { // no nodes yet
// nothing in bucket
current = new Node(); // construct single node
current->key = key; // set key (username)
current->value = value; // set value (password)
current->next = head; // add value to head of list
hashTable[tableIndex] = current; // update current to point to front of list
return; // exit
} else {
while(current->next != nullptr) {
current = current->next; // advance to next node
} // end while
// currently at node we want to insert key/value at
current = new Node();
current->key = key; // set key(username)
current->value = value; // set value (pw)
current->next = nullptr; // set next to point to nullptr
} // end inner if-else (creates node)
} // end outer if-else
} // end add
bool contains(const std::string& key) const
{
unsigned int hashedValue = hashFunc(key); // hash the key given to get an index
unsigned int tableIndex = getTableIndex(hashedValue); // get the table index
Node* current = hashTable[tableIndex];
if(current == nullptr) { // if there are no nodes at given index
return false;
} else { // there are some nodes in the hash table
while(current != nullptr && current->key != key) {
current = current->next;
} // end while
if(current == nullptr) { // we reached the end of our linked list
return false; // couldn't find a value
} else { // we found the key provided
return true;
}
} // end if-else
}
std::string value(const std::string& key) const
{
if(contains(key) == true) { // found match
unsigned int hashedValue = hashFunc(key); // hash the key given to get an index
unsigned int tableIndex = getTableIndex(hashedValue); // get the table index
Node* current = hashTable[tableIndex];
while(current != nullptr && current->key != key) {
current = current->next;
}
return current->value; // return value after traversal
} else {
return ""; // no match, return empty string
}
}
}; // end class
/* main.cpp */
int main()
{
// initialize test
HashMap test1;
std::cout << "TEST 1 HASHMAP OBJECT CREATED" << std::endl;
// add some values
std::string key1 = "Alex";
std::string value1 = "password1";
std::string key2 = "Danielle";
std::string value2 = "password2";
std::cout << "strings have been created" << std::endl;
// add to hash map
test1.add(key1, value1);
test1.add(key2, value2);
std::cout << "Keys and values have been added to hash map" << std::endl;
// does key1 contain the word "hi"? no, should return false (0)
std::cout << "Hash map contains word hi?: " << test1.contains("hi") << std::endl;
// does key2 contain word "Danielle"? yes, should return true (1)
std::cout << "Hash map contains word Danielle?: " << test1.contains("Danielle") << std::endl;
// copy constructor test
HashMap test2{test1};
test2.add("Chicken", "lizard1");
std::cout << "Password for user Chicken: " << test2.value("Chicken") << std::endl;
HashMap test3 = test2;
// pw should stay lizard1 (doesn't work rn)
std::cout << "Password for user Chicken after copy: " << test3.value("Chicken") << std::endl;
// check to see if we get value
std::cout << "Password for user " << key1 << ": " << test1.value(key1) << std::endl;
std::cout << "Password for user " << key2 << ": " << test1.value(key2) << std::endl;
// should return empty string
std::cout << "Test of incorrect password for invalid user INVALID: " << test1.value("lol") << std::endl;
return 0;
}
您可以在默认的ctor中写入注释
// initialize each node to nullptr from the array of buckets to nullptr
然后继续做一些完全不同的事情。而另一件事是完全错误的。初始化所有节点PTR以指向同一节点。这意味着在销毁时,您将多次删除同一节点,然后崩溃并烧掉
同时修复该警告
在修复明显错误后,更新新版本的代码,该代码有一个损坏的复制构造函数,并且没有赋值运算符
hashTable[i] = hm.hashTable[i];
将使两个哈希表共享数据元素。你想要复制,而不是分享。您希望复制所有存储桶,而不是sz存储桶—您从未更新sz,但这是另一个问题。您的初始化看起来正常。如果没有看到MCVE,很难判断valgrind的问题是什么。除此之外,我希望您不要在现实世界的程序中真正存储密码或编写自己的哈希映射。@n.m.MCVE只是valgrind输出的任何东西吗?在这种情况下,我可以添加一个编辑通过它。另一方面,这只是一个类赋值:没有一个MCVE是复制问题的完整可构建源代码。我可以复制、粘贴、编译、运行并查看您看到的错误,而无需跳转。@n.m.对此表示抱歉。下面是我正在使用的完整代码。不要发布代码档案的链接,将代码作为单个文件内联发布在问题文本中。这意味着您需要努力将代码缩减为一个大小合理的文件。那么,如何在每个bucket索引处初始化列表呢?我最初设置了hashTable[I]=nullptr,我原本认为这是可行的,但实际上并没有。我是否必须在for循环的每次迭代中创建某种初始节点,并将其设置为等于索引I处的哈希表?请确保您发布的代码是编译的。您缺少include指令,并且没有在任何地方定义getTableIndex。在发布一个代码块之前,请自己编译它,并验证您是否获得了预期的输出。请不要在游戏中途更改代码,以免使答案无效。在修复明显错误后,您的当前版本有一个损坏的复制构造函数,并且没有赋值运算符。