将值添加到单独链接的哈希表C++; 我对C++仍然很陌生,而且在实现一个实际工作的Add方法时遇到了麻烦。我在java中做了一个哈希图,但是把它翻译成C++已经证明是困难的。除此之外,我还必须处理约束(例如不更改头文件中的任何内容),并且不能使用std::string和std::cout/cin之外的任何std库函数
基本上,我必须创建一个散列映射,它最终将存储用户名(作为密钥)和密码(作为值)。此时,用户名/密码组合并不那么重要,因为实现一个非常通用的类是练习的重点 只是尝试使用默认构造函数测试HashMap,然后添加一个值,结果导致出现分段错误。我100%确信,在尝试实现此哈希表时,我犯了一些可怕的错误。要么我没有正确地将bucket索引与节点连接起来,要么我没有正确地初始化某些内容 以下是我正在使用的头文件:将值添加到单独链接的哈希表C++; 我对C++仍然很陌生,而且在实现一个实际工作的Add方法时遇到了麻烦。我在java中做了一个哈希图,但是把它翻译成C++已经证明是困难的。除此之外,我还必须处理约束(例如不更改头文件中的任何内容),并且不能使用std::string和std::cout/cin之外的任何std库函数,c++,class,hash,hashmap,hashtable,C++,Class,Hash,Hashmap,Hashtable,基本上,我必须创建一个散列映射,它最终将存储用户名(作为密钥)和密码(作为值)。此时,用户名/密码组合并不那么重要,因为实现一个非常通用的类是练习的重点 只是尝试使用默认构造函数测试HashMap,然后添加一个值,结果导致出现分段错误。我100%确信,在尝试实现此哈希表时,我犯了一些可怕的错误。要么我没有正确地将bucket索引与节点连接起来,要么我没有正确地初始化某些内容 以下是我正在使用的头文件: #ifndef HASHMAP_HPP #define HASHMAP_HPP #inclu
#ifndef HASHMAP_HPP
#define HASHMAP_HPP
#include <functional>
#include <string>
class HashMap
{
public:
// Hash functions must conform to these properties:
//
// (1) Given a particular string s repeatedly, they must always
// return the same hash value.
// (2) They do not take the number of buckets into account (as they
// do not receive a parameter that tells them how many buckets
// there are). Any unsigned int value is fair game as a result.
// It will be the job of the HashMap class to reduce the results
// to the range of available bucket indices (e.g., by using the
// % operator).
typedef std::function<unsigned int(const std::string&)> HashFunction;
// This constant specifies the number of buckets that a HashMap will
// have when it is initially constructed.
static constexpr unsigned int initialBucketCount = 10;
public:
// This constructor initializes the HashMap to use whatever default
// hash function you'd like it to use. A little research online will
// yield some good ideas about how to write a good hash function for
// strings; don't just return zero or, say, the length of the string.
HashMap();
// This constructor instead initializes the HashMap to use a particular
// hash function instead of the default. (We'll use this in our unit
// tests to control the scenarios more carefully.)
HashMap(HashFunction hasher);
// The "Big Three" need to be implemented appropriately, so that HashMaps
// can be created, destroyed, copied, and assigned without leaking
// resources, interfering with one another, or causing crashes or
// undefined behavior.
HashMap(const HashMap& hm);
~HashMap();
HashMap& operator=(const HashMap& hm);
// add() takes a key and a value. If the key is not already stored in
// this HashMap, the key/value pair is added; if the key is already
// stored, the function has no effect.
//
// If adding the new key/value pair will cause the load factor of this
// HashMap to exceed 0.8, the following must happen:
//
// (1) The number of buckets should be increased by doubling it and
// adding 1 (i.e., if there were 10 buckets, increase it to
// 2 * 10 + 1 = 21).
// (2) All key/value pairs should be rehashed into their new buckets,
// important because changing the number of buckets will likely
// change which bucket a particular key hashes to (especialy if
// you're using % to determine the index of that bucket).
void add(const std::string& key, const std::string& value);
// remove() takes a key and removes it (and its associated value) from
// this HashMap if it is already present; if not, the function has no
// effect.
void remove(const std::string& key);
// contains() returns true if the given key is in this HashMap, false
// if not.
bool contains(const std::string& key) const;
// value() returns the value associated with the given key in this HashMap
// if the key is stored in this HashMap; if not, the empty string is
// returned. (Going forward, we'll discover that throwing an exception
// is a better way to handle the scenario where the key is not present,
// but we'll conquer that at a later date.)
std::string value(const std::string& key) const;
// size() returns the number of key/value pairs stored in this HashMap.
unsigned int size() const;
// bucketCount() returns the number of buckets currently allocated in
// this HashMap.
unsigned int bucketCount() const;
// loadFactor() returns the proportion of the number of key/value pairs
// to the number of buckets, a measurement of how "full" the HashMap is.
// For example, if there are 20 key/value pairs and 50 buckets, we would
// say that the load factor is 20/50 = 0.4.
double loadFactor() const;
// maxBucketSize() returns the number of key/value pairs stored in this
// HashMap's largest bucket.
unsigned int maxBucketSize() const;
private:
// This structure describes the nodes that make up the linked lists in
// each of this HashMap's buckets.
struct Node
{
std::string key;
std::string value;
Node* next;
};
// Store the hash function (either the default hash function or the one
// passed to the constructor as a parameter) in this member variable.
// When you want to hash a key, call this member variable (i.e., follow
// it with parentheses and a parameter) just like you would any other
// function.
HashFunction hasher;
// You will no doubt need to add at least a few more private members
public:
// our hash function
unsigned int hashFunc(const std::string& key) const;
private:
Node** hashTable;
// We need a variable that will always let us know what the current amount
// of buckets is. bucketCount will use this and return this variable.
unsigned int amountOfBuckets;
// we also need the number of keys currently in the hash map. This is stored here
unsigned int sz;
};
#endif // HASHMAP_HPP
基本上,分段错误发生在add函数中。那么add到底是怎么回事?我如何理解哈希映射应该做得更好呢?在HashMap::add中,您正在取消对空指针的引用。您在构造函数中创建了一个大小为10个元素的节点指针数组,但在代码中从未创建任何节点对象并分配给数组中的指针。所以当你这样做的时候:
current->key = key;
您正在访问的节点指针为0。您自己的当前值应等于
nullptr
的注释正确地预示了下一行的故障:
// current should equal a nullptr
current->key = key; // set key (user)
假设新分配的数组将充满nullptr
通常是不好的做法。
您需要在构造函数中将其设置为allnullptr
add()
函数还有其他问题
这里有一个尝试,使它至少适合用途:
void HashMap::add(const std::string& key, const std::string& value)
{
// Check if key being stored matches key already in hashmap
/* BASIC ADD FUNCTION, JUST TO IMPLEMENT UNIT TESTS */
unsigned int hashVal = hashFunc(key);
Node* head=hashTable[hashVal];
Node* current = head;
if(current==nullptr){
//Nothing in this bucket so it's definitely new.
current=new Node();
current->key = key; // set key (user)
current->value = value; // set password
current->next = nullptr; // set the next ptr to be nullptr.
hashTable[hashVal]=current;
return;
}
do { //It's a do-while because the if statement above has handled current==nullptr.
if(current->key==key){
//We've found a match for the key.
//Common hash-table behavior is to overwrite the value. So let's do that.
current->value=value;
return;
}
current = current->next; // move current to the next node
} while(current != nullptr) // stop once we go past the last node.
//Finally, we found hash collisions but no match on key.
//So we add a new node and chain it to the node(s) already there.
//
//Sometimes it's a good idea to put the new one at the end or if it's likely to get looked up
//it's also a good idea to put it at the start.
//It might be a good idea to keep the collision chain sorted and insert into it accordingly.
//If we sort we can dive out of the loop above when we pass the point the key would be.
//
//However for a little example like this let's put the new node at the head.
current=new Node();
current->key = key; // set key (user)
current->value = value; // set password
current->next = head; // set the next pointer to be the old head.
hashTable[hashVal]=current;
}
PS:你的哈希函数也有问题。
在主循环中取散列表大小的模。
这只会限制分发,并造成可怕的传播。
至少将其移动到该函数的最后一行:
return hashValue % bucketCount() ;
unsigned int hashVal = hashFunc(key); //Assume modified to not use bucketCount().
unsigned int tableIndex=hashVal % bucketCount();//Reduce hash to a valid index.
Node* head=hashTable[tableIndex]; //TODO: Do same in the other accesses to hashTable...
但是,我建议将其移动到add
功能:
return hashValue % bucketCount() ;
unsigned int hashVal = hashFunc(key); //Assume modified to not use bucketCount().
unsigned int tableIndex=hashVal % bucketCount();//Reduce hash to a valid index.
Node* head=hashTable[tableIndex]; //TODO: Do same in the other accesses to hashTable...
然后,您可以将完整的哈希值存储在节点
结构中,并在current->key==key
之前将其用作更强的预比较。如果哈希表可能非常满,您可以获得很大的性能增益。这取决于是否要为每个节点的无符号int
预留字节。
如果您这样做了,并且接受了对冲突链进行排序的提示,那么您可以通过哈希代码进行排序,并且通常可以避免在add()
new key或get()
处比较任何字符串,并且通常只在成功的get()
或覆盖add()中比较一次
如何使节点指针指向某个数组值,指向新的节点对象?我认为Node*current=hashTable[hashVal];我会那样做的。你说的有道理,我只是想不出一种方法让它工作:(你的表是一个指针数组。指针都是空的。你成功地从表中得到了一个空指针,但这无助于写回表中。你应该将哈希转换成一个bucket索引,然后存储哈希表[bucket]=新节点(…);
unsigned int hashVal = hashFunc(key); //Assume modified to not use bucketCount().
unsigned int tableIndex=hashVal % bucketCount();//Reduce hash to a valid index.
Node* head=hashTable[tableIndex]; //TODO: Do same in the other accesses to hashTable...