轉載自https://www.sczyh30.com/posts/C-C/cpp-stl-hashmap/
C++ STL中,哈希表對應的容器是unordered_map
(since C++ 11)。根據 C++ 11 標準的推薦,用 unordered_map
代替 hash_map
。
Prologue
先來回顧一下數據結構中哈希表相關的知識。
哈希表是根據關鍵碼值(key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度,這個映射函數叫做散列函數。
哈希表的一個重要問題就是如何解決映射衝突的問題。常用的有兩種:開放地址法 和 鏈地址法。
與 map
的區別
STL中,map
對應的數據結構是 紅黑樹。紅黑樹是一種近似於平衡的二叉查找樹,裏面的數據是有序的。在紅黑樹上做查找操作的時間複雜度爲 O(logN)。而 unordered_map
對應 哈希表,哈希表的特點就是查找效率高,時間複雜度爲常數級別 O(1), 而額外空間複雜度則要高出許多。所以對於需要高效率查詢的情況,使用 unordered_map
容器。而如果對內存大小比較敏感或者數據存儲要求有序的話,則可以用 map
容器。
基本使用
unordered_map
的用法和 map
大同小異,一個簡單示例:
#include <iostream>
#include <unordered_map>
#include <string>
int main(int argc, char **argv) {
std::unordered_map<int, std::string> map;
map.insert(std::make_pair(1, "Scala"));
map.insert(std::make_pair(2, "Haskell"));
map.insert(std::make_pair(3, "C++"));
map.insert(std::make_pair(6, "Java"));
map.insert(std::make_pair(14, "Erlang"));
std::unordered_map<int, std::string>::iterator it;
if ((it = map.find(6)) != map.end()) {
std::cout << it->second << std::endl;
}
return 0;
}
使用自定義類
要使用哈希表,必須要有對應的計算散列值的算法以及判斷兩個值(或對象)是否相等的方法。
在 Java 中,Object 類裏有兩個重要方法:hashCode 和 equals 方法。其中 hashCode 方法便是爲散列存儲結構服務的,用來計算散列值;而 equals 方法則是用來判斷兩對象是否等價。由於所有的類都繼承自 java.lang.Object 類,因此所有類相當於都擁有了這兩個方法。
而在 C++ 中沒有自動聲明這類函數,STL 只爲 C++ 常用類提供了散列函數,因此如果想在 unordered_map 中使用自定義的類,則必須爲此類提供一個哈希函數和一個判斷對象是否相等的函數(e.g. 重載 == 運算符)。下面是一個簡單示例(扒自數據結構上機作業的部分代碼):
using std::string;
using std::cin;
using std::cout;
using std::endl;
using std::unordered_map;
class Person {
public:
string phone;
string name;
string address;
explicit Person() {}
explicit Person(string name, string phone, string address): name(name), phone(phone), address(address) {}
// overload operator==
bool operator==(const Person& p) {
return this->phone == p.phone && this->name == p.name
&& this->address == p.address;
}
inline friend std::ostream& operator<<(std::ostream& os, Person& p) {
os << "[Person] -> (" << p.name << ", " << p.phone << ", "
<< p.address << ")";
return os;
}
};
// declare hash<Person>
namespace std {
template <>
struct hash<Person> {
std::size_t operator()(const Person& p) const {
using std::size_t;
using std::hash;
using std::string;
// Compute individual hash values for first,
// second and third and combine them using XOR
// and bit shifting:
return ((hash<string>()(p.phone)
^ (hash<string>()(p.name) << 1)) >> 1)
^ (hash<string>()(p.address) << 1);
}
};
}
unordered_map<string, Person> phoneMap;
void selectByPhone() {
string phone;
cout << "Input the phone number: "; cin >> phone;
unordered_map<string, Person>::iterator it;
int size = phoneMap.size();
for(int pc = 0; pc < size; pc++) {
if((it = phoneMap.find(phone)) != phoneMap.end()) {
cout << "Query result: " << it->second << endl;
return;
}
}
cout << "Query result : target_not_found" << endl;
}