C++ STL中,哈希表
对应的容器是 unordered_map
(since C++ 11)。
定义
template < class Key, // unordered_map::key_typeclass T, // unordered_map::mapped_typeclass Hash = hash<Key>, // unordered_map::hasherclass Pred = equal_to<Key>, // unordered_map::key_equalclass Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type> class unordered_map;
- Key
Type of the key values. Each element in an unordered_map is uniquely identified by its key value.
Aliased as member type unordered_map::key_type. - T
Type of the mapped value. Each element in an unordered_map is used to store some data as its mapped value.
Aliased as member type unordered_map::mapped_type. Note that this is not the same as unordered_map::value_type (see below). - Hash
A unary function object type that takes an object of type key type as argument and returns a unique value of type size_t based on it. This can either be a class implementing a function call operator or a pointer to a function (see constructor for an example). This defaults to hash, which returns a hash value with a probability of collision approaching 1.0/std::numeric_limits<size_t>::max().
The unordered_map object uses the hash values returned by this function to organize its elements internally, speeding up the process of locating individual elements.
Aliased as member type unordered_map::hasher. - Pred
A binary predicate that takes two arguments of the key type and returns a bool. The expression pred(a,b), where pred is an object of this type and a and b are key values, shall return true if a is to be considered equivalent to b. This can either be a class implementing a function call operator or a pointer to a function (see constructor for an example). This defaults to equal_to, which returns the same as applying the equal-to operator (a==b).
The unordered_map object uses this expression to determine whether two element keys are equivalent. No two elements in an unordered_map container can have keys that yield true using this predicate.
Aliased as member type unordered_map::key_equal. - Alloc
Type of the allocator object used to define the storage allocation model. By default, the allocator class template is used, which defines the simplest memory allocation model and is value-independent.
Aliased as member type unordered_map::allocator_type.
unordered_map的原理
hashtable + bucket
由于 unordered_map 内部采用 hashtable 的数据结构存储,所以,每个特定的 key 会通过一些特定的哈希运算映射到一个特定的位置,我们知道,hashtable 是可能存在冲突的,在同一个位置的元素会按顺序链在后面(也就是使用拉链法解决哈希冲突)。所以把这个位置称为一个 bucket 是十分形象的,每个哈希桶中可能没有元素,也可能有多个元素。
unordered_map的插入过程:
1、得到 key;
2、通过 hash 函数得到 key对应的 hash 值;
3、得到桶号(一般都为 hash 值对桶数求模);
4、存放 key 和 value 在桶内(发生冲突,用比较函数解决)。
unordered_map的取值过程:
1、得到 key
2、通过 hash 函数得到 key对应的 hash 值
3、得到桶号(一般都为 hash 值对桶数求模)
4、比较桶内部是否有与 key 相等的元素,若都不相等,则没有找到;若存在,则取出相等的记录的。
总结其特点如下:
关联性:通过 key 去检索 value,而不是通过绝对地址(和顺序容器不同)
无序性:使用 hash 表存储,内部无序
Map : 每个值对应一个键值(unordered_map<Key, Value> 的元素类型是 std::pair<const Key, Value>。如果有某个元素的Value部分的地址,减去 offsetof(std::pair<const Key, Value>, second) 再加上 offsetof(std::pair<const Key, Value>, first) (虽然估计是 0,不加也没事),就是对应的 Key 部分的地址)
键唯一性:不存在两个元素的 key 一样(unordered_multimap 可以存放相同相同 key)
动态内存管理:使用内存管理模型来动态管理所需要的内存空间
unorder_map 与 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;
}
引用自:
- C++ unordered_map
- C++ STL 之哈希表 | unordered_map
- std::Unordered_map