C++ STL中,哈希表
对应的容器是 unordered_map
(since C++ 11)。
template < class Key, // unordered_map::key_type
class T, // unordered_map::mapped_type
class Hash = hash<Key>, // unordered_map::hasher
class Pred = equal_to<Key>, // unordered_map::key_equal
class Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type
> class 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 一样(unordered_multimap 可以存放相同相同 key)
动态内存管理:使用内存管理模型来动态管理所需要的内存空间
STL中,map 对应的数据结构是红黑树
。红黑树是一种近似于平衡的二叉查找树,里面的数据是有序的。在红黑树上做查找操作的时间复杂度为 O(logN)
。而 unordered_map 对应 哈希表
,哈希表的特点就是查找效率高,时间复杂度为常数级别 O(1)
, 而额外空间复杂度则要高出许多。所以对于需要高效率查询的情况,使用 unordered_map 容器。而如果对内存大小比较敏感或者数据存储要求有序的话,则可以用 map 容器。
基本使用
unordered_map 的用法和 map 大同小异,一个简单示例:
#include
#include
#include
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
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;
}
引用自: