2014-11-23 844 views
0

節點的值,我應該創建一個存儲用戶名和密碼,這是作爲字符串存儲的哈希映射。這個哈希映射是分開鏈接的。當增加一個值或搜索值,我應該使用哈希函數對返回一個整數,那麼這將給我要鬥我需要用一個函數的索引中的密鑰。如果添加一些東西,我將它存儲在列表的末尾。初始化在構造函數中的哈希表C++

所以散列圖(在我心中)是這樣的:

Bucket | List 
[0] -> nullptr 
[1] -> (key1/value1) -> (key2/value2) 
[2] -> (key3/value3) 

每個鍵/值組合在技術上是一個節點,其具有不可修改的結構,我的頭文件中,就像這樣:

struct Node 
{ 
    std::string key; 
    std::string value; 
    Node* next; 
}; 

儘管我的添加和搜索功能在技術上有效,但我想在構造函數中正確初始化我的哈希映射,以便我對而不是對未初始化的值進行比較。這是我現在的構造是如何:

HashMap::HashMap() 
    : hashTable{new Node*[initialBucketCount]}, amountOfBuckets{initialBucketCount}, sz{0} 
{ 
    for(int i = 0; i < amountOfBuckets; i++) { 
     hashTable[i] = nullptr; 
    } 
} 

的問題是,是,當我要比較的東西,就像在我的附加功能,我得到的valgrind一個錯誤,說我做一個條件跳轉或移動未初始化的值。例如,在添加這樣做:

Node* current = hashTable[tableIndex]; 
if(current == nullptr) 
    return false; 

會說if(current == 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; 
} 
+0

您的初始化看起來不錯。沒有看到MCVE,很難說valgrind的問題是什麼。 (另外,我希望你不會在真實世界的程序中存儲密碼或編寫自己的散列表)。 – 2014-11-23 04:31:09

+0

@ n.m。 MCVE是否只是valgrind的輸出?在這種情況下,我可以添加一個編輯。在一個側面說明,這只是一個類的作業:) – Alex 2014-11-23 04:34:43

+0

沒有MCVE是完整的可重構的源代碼,重現了這個問題。我可以複製,粘貼,編譯,運行並查看您所看到的錯誤,而無需跳過箍環。 – 2014-11-23 04:38:29

回答

0

你寫在你的默認評論構造函數

// initialize each node to nullptr from the array of buckets to nullptr 

,然後繼續做別的東西完全。而另一件事是完全錯誤的。您將所有節點ptrs初始化爲指向同一個節點。這意味着在銷燬時你會多次刪除同一個節點,並導致崩潰和燒燬。

而且解決這個問題的警告。

更新新版本的代碼在修復了明顯的錯誤之後,具有破壞的拷貝構造函數並且沒有賦值運算符。

hashTable[i] = hm.hashTable[i]; 

將使兩個散列表共享數據元素。你想要複製,而不是分享。你想複製所有的桶,而不是sz桶(你永遠不會更新sz,但這是另一個問題)。

+0

那麼,我將如何結束在每個桶索引初始化列表?我最初設置了hashTable [i] = nullptr,我原本認爲它工作,但它確實沒有。我需要在for循環的每次迭代中創建一些初始節點,並將它設置爲等於索引i處的hashTable? – Alex 2014-11-23 07:17:06

+0

請確保您發佈的代碼是編譯的。你缺少include指令,並且在任何地方都沒有定義'getTableIndex'。在發佈代碼塊之前,請自行編譯並驗證您是否獲得了預期的輸出。也請儘量不要在遊戲中間更改代碼,以免使答案失效。在修復明顯的錯誤之後,您的當前版本具有損壞的複製構造函數並且沒有賦值運算符。 – 2014-11-23 07:56:05