1.C++ STL unordered_map用法
在C++11中,unordered_map作为一种关联容器,替代了hash_map,unordered_map的底层实现是hash表,所以被称为无序关联容器。使用时需要指明头文件 #include<map>。
不管是map还是unordered_map都是一种 key-map(value) 映射的容器,提供非常高的查找效率。
2.成员
2.1 类型定义
-
unordered_map::const_iterator 受控序列的常量迭代器。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); for (Mymap::const_iterator it = c1.begin(); it != c1.end(); ++it) std::cout << " [" << it->first << ", " << it->second << "]";
output: [a,1],[b,2],[c,3]
-
unordered_map :: const_local_iterator 受控序列的常量存储桶迭代器。用法和const_iterator类似。
-
unordered_map :: const_pointer 指向元素的常量指针。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); for (Mymap::iterator it = c1.begin(); it != c1.end(); ++it) { Mymap::const_pointer p = &*it; std::cout << " [" << p->first << ", " << p->second << "]"; }
output: [a,1],[b,2],[c,3]
-
unordered_map :: const_reference 该类型描述了一个对象,该对象可用作对受控序列元素的恒定引用。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); for (Mymap::iterator it = c1.begin(); it != c1.end(); ++it) { Mymap::const_reference ref = *it; std::cout << " [" << ref.first << ", " << ref.second << "]"; }
output: [a,1],[b,2],[c,3]
-
unordered_map::hasher 哈希函数。
Mymap c1; Mymap::hasher hfn = c1.hash_function(); std::cout << "hfn('a') == " << hfn('a') << std::endl; std::cout << "hfn('b') == " << hfn('b') << std::endl;
output: hfn('a') == 3826002220 hfn('b') == 3876335077
-
unordered_map :: iterator 受控序列的迭代器类型。用法和const_iterator类似。
-
unordered_map :: key_equal 比较函数。
Mymap c1; Mymap::key_equal cmpfn = c1.key_eq(); std::cout << "cmpfn('a', 'a') == " << std::boolalpha << cmpfn('a', 'a') << std::endl; std::cout << "cmpfn('a', 'b') == " << std::boolalpha << cmpfn('a', 'b') << std::endl;
output: true, false
2.2 成员函数
- begin 指定受控序列或存储桶的开始。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); for (Mymap::const_iterator it = c1.begin(); it != c1.end(); ++it) std::cout << " [" << it->first << ", " << it->second << "]";
output: [a,1],[b,2],[c,3]
- bucket 获取键值的存储桶(存储区)编号。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); Mymap::size_type bs = c1.bucket('a'); std::cout << "bucket('a') == " << bs << std::endl;
- clear 删除所有元素。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); c1.clear(); std::cout << "size == " << c1.size() << std::endl; std::cout << "empty() == " << std::boolalpha << c1.empty() << std::endl;
output: size == 0 empty()== true
- count 查找与指定键匹配的元素数。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); std::cout << "count('A') == " << c1.count('A') << std::endl; std::cout << "count('b') == " << c1.count('b') << std::endl;
output: count('A')== 0 count('b')== 1
- empty 判断是否为空。
-
erase 删除指定位置的元素。两种形式:
iterator erase(iterator where); // 要移除的位置通过where来指定 iterator erase(iterator first, iterator last); // 移除[first, last)范围的元素
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); Mymap::iterator it2 = c1.erase(c1.begin()); std::cout << "*erase(begin()) == [" << it2->first << ", " << it2->second << "]"; c1.insert(Mymap::value_type('d', 4)); c1.insert(Mymap::value_type('e', 5)); it2 = c1.end(); it2 = c1.erase(c1.begin(), --it2); std::cout << "*erase(begin(), end()-1) == [" << it2->first << ", " << it2->second << "]" << std::endl;
[b,2] [e,5]
- find 查找与指定键匹配的元素。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); std::cout << "find('A') == " << std::boolalpha << (c1.find('A') != c1.end()) << std::endl; std::cout << "find('b') == " << std::boolalpha << (it != c1.end()) << ": [" << it->first << ", " << it->second << "]" << std::endl;
find('A')==false find('b')== true
- end 指定受控序列的结尾。注意:它所指的位置是迭代器最后一个元素位置的下一个位置。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); Mymap::iterator it2 = c1.end(); --it2; std::cout << " [" << it2->first << ", " << it2->second << "]"; --it2; std::cout << " [" << it2->first << ", " << it2->second << "]";
[c,3] [b,2]
- hash_function 获取存储的哈希函数对象。
- insert 添加元素。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3));
- key_eq 判断key是否相等。
- swap 交换两个容器的元素。
Mymap c1; c1.insert(Mymap::value_type('a', 1)); c1.insert(Mymap::value_type('b', 2)); c1.insert(Mymap::value_type('c', 3)); for (Mymap::const_iterator it = c1.begin();it != c1.end(); ++it) std::cout << " [" << it->first << ", " << it->second << "]"; std::cout << std::endl; Mymap c2; c2.insert(Mymap::value_type('d', 4)); c2.insert(Mymap::value_type('e', 5)); c2.insert(Mymap::value_type('f', 6)); c1.swap(c2); for (Mymap::const_iterator it = c1.begin();it != c1.end(); ++it) std::cout << " [" << it->first << ", " << it->second << "]"; std::cout << std::endl; swap(c1, c2); for (Mymap::const_iterator it = c1.begin();it != c1.end(); ++it) std::cout << " [" << it->first << ", " << it->second << "]"; std::cout << std::endl;
[a,1],[b,2],[c,3] [d,4],[e,5],[f,6] [a,1],[b,2],[c,3]
3. 哈希表的查找
理想的情况是希望不经过任何比较,一次存取便能得到所查记录,那就必须在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和结构中一个惟一的存储位置相对应。因而在查找时,只要根据这个对应关系f找到给定值K的像f(K)。若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上,由此,不需要进行比较便可直接取得所查记录。在此,我们称这个对应关系f为哈希( Hash)函数,按这个思想建立的表为哈希表。
哈希函数的构造方法
哈希函数是从关键字集合到地址集合的映像。通常,关键字集合比较大,它的元素包括所有可能的关键字,而地址集合的元素仅为哈希表中的地址值。哈希函数其实是一个压缩映像,那么这种情况就不可避免的产生冲突,那么在建造哈希表时不仅要设定一个好的哈希函数,还要设定一种处理冲突的方法。
-
直接定址法
取关键字或关键字的某个线性函数值为哈希地址。即H(key)=key 或 H(key)=a*key+b (a,b为常数)。
举例:统计解放以后出生人口,其中年份作为关键字,哈希函数取关键字自身加一个常数H(key)=key+(-1948).查找1970年出生的人数,则直接查(1970-1948)=22项即可。
地址 01 02 03 ... 22 23 24 ...
年份 1949 1950 1951 ... 1970
人数 ............. 15000
-
除留余数法
取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址(p为素数)
H(key)=key MOD p,p<=m (最简单,最常用)p的选取很重要
一般情况,p可以选取为质数或者不包含小于20的质因数的合数(合数指自然数中除了能被1和本身整除外,还能被其他数(0除外)整除的数)。
-
数字分析法
-
平方取中法
-
折叠法
-
随机数法
常用冲突处理方式
-
开放定址法
这个方法的基本思想是:当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。这个过程可用下式描述:
H i ( key ) = ( H ( key )+ d i ) mod m ( i = 1,2,…… , k ( k ≤ m – 1))
其中: H ( key ) 为关键字 key 的直接哈希地址, m 为哈希表的长度, di 为每次再探测时的地址增量。
采用这种方法时,首先计算出元素的直接哈希地址 H ( key ) ,如果该存储单元已被其他元素占用,则继续查看地址为 H ( key ) + di 的存储单元,如此重复直至找到某个存储单元为空时,将关键字为 key 的数据元素存放到该单元。
增量 d 可以有不同的取法,并根据其取法有不同的称呼:
( 1 ) d i = 1 , 2 , 3 , …… 线性探测再散列;
( 2 ) d i = 1^2 ,- 1^2 , 2^2 ,- 2^2 , k^2, -k^2…… 二次探测再散列;
( 3 ) d i = 伪随机序列 伪随机再散列;
-
再散列函数法:(再哈希法)遇到冲突就重新采用一个散列函数计算新的存储位置,可以使关键字不产生聚集
-
链地址法(拉链)将所有关键字的同义词记录在一个单链表中,在散列表中只存储所有同义词表的头指针
-
建立一个公共溢出区法: 为所有冲突的关键字开辟一个公共的溢出区(表)来存放
4. 代码
哈希表采用数组存储,哈希函数构造和处理冲突的方法是除留余数法+开放定址法
/**** * Hash Table * ****/ //#include "Global.h" #include <iostream> using namespace std; // HashTable Data Structure Definition // array hashtable #define tablesize 10 typedef int HashTable[tablesize]; //hash function initialization way void Initial_HashTable(HashTable &ht) { for (int i = 0; i < tablesize; i++) ht[i] = 0; } //search hashtable function int Search_HashTable(HashTable &ht,int key) { int address = key%tablesize; int compare = 0; while (compare < tablesize&&ht[address] != key&&ht[address] != 0) { compare++; address = (address+1)%tablesize; } if (compare == 10 || ht[address] == 0) cout << "can not find elem" << endl; return address; } //insert hashtable function void Insert_HashTable(HashTable &ht,int key) { int res = Search_HashTable(ht,key); if (ht[res] == 0) ht[res] = key; } //test function int main() { int data[8] = { 25,36,39,47,20,58,16,35 }; HashTable ht; //initialization. Initial_HashTable(ht); //insert datas. for (int i = 0; i < 8; i++) { Insert_HashTable(ht, data[i]); } cout << endl; //search. cout << "25 : " << Search_HashTable(ht, 25) << endl; cout << "35 : " << Search_HashTable(ht, 35) << endl; cout << "145 : " << Search_HashTable(ht, 145) << endl; system("pause"); return 0; }
5. 参考
https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/bb982522(v=vs.90)
https://www.cnblogs.com/mydomain/p/11205623.html
https://www.cnblogs.com/ajianbeyourself/p/4358672.html