1 map自定义排序
cpp
#include <map>
#include <iostream>
#include <functional>
using namespace std;
int main() {
function<bool(pair<int, int>, pair<int, int>)> cmp = [&](pair<int, int> p1, pair<int, int> p2) -> bool {
return p1.second < p2.second;
};
map< pair<int, int>, int, decltype(cmp)> mp({ {{5, 1}, 2}, {{1, 7}, 3}, {{7, 3}, 2}, {{2, 2}, 1} },cmp);
//map<pair<int, int>, int> mp = { {{5, 2}, 2}, {{1, 2}, 3}, {{7, 2}, 2}, {{2, 2}, 1} };
for (auto& p : mp) {
cout << p.first.first << ' ' << p.first.second << ' ' << p.second << '\n';
}
}
map的类模板参数
map的构造函数
2 map/unordered_map的下标操作
2.1 at()
访问关键字为k的元素,带参数检查:若k不存在容器中,抛出一个out_of_range
异常
2.2 下标运算符与解引用迭代器的区别
下标运算符:调用insert
返回pair<iterator, bool>
用返回的迭代器访问mapped_type
cpp
V& operator[](const K& key) {
pair<iterator, bool> ret = _t.insert(make_pair(key, V()));
return ret.first->second;
}
解引用迭代器返回pair<key_type, mapped_type>
2.3 对比vector与map的下标运算符
cpp
map<int, int> m;
m[0] = 1;
vector<int> v;
v[0] = 1;
2.4 对map使用find替代下标操作
使用下标操作有一个严重的副作用:如果关键字还未在map中,下标操作会插入一个具有给定关键字的元素(具体原因可以参考2.2节给出的代码),其值为0;
3 用lower_bound、upper_bound、equal_range查找multimap中的元素
- 用lower_bound与upper_bound锁定范围
cpp
//authoers的key为作者,mapped为书名
//search_item表示要查找的作者
for (auto beg = authors.lower_bound(search_item), end = authors.upper_bound(search_item); beg != end; ++beg) {
cout << beg->second << endl;
}
- 用equal_range锁定范围
equal_range接受一个关键字,返回一个迭代器pair,若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置
cpp
for (auto pos = authors.equal_range(search_item); pos.first != pos.second; ++pos.first) {
cout << pos.first.second << endl;
}
4 将自定义类型作为无序容器的关键字
默认情况下,无序容器使用关键字类型的==运算符来比较元素,他们还使用一个hash<key_type>类型的对象(仿函数)来生成每个元素的哈希值。标准库为内置类型(包括指针)提供了hash模板,还为一些标准库类型,比如string和智能指针类型定义了hash。
那么如何定义关键字为自定义类型的无序容器呢?
4.1类模板特化
cpp
namespace std {
template<>
struct hash<Sales_data> {
//用来散列一个无需容器的类型必须要定义下列类型
typedef size_t result_type;
typedef Sales_data argument_type; //默认情况下,此类型需要operator==
size_t operator()(const Sales_data& s) const {
return hash<string>() (s.bookNo) ^ //构造匿名对象调用operator()返回一个哈希值
hash<unsigned>() (s.units_sold) ^
hash<double>() (s.revenue);
}
}
} //关闭std命名空间
4.2 通过类模板传参
cpp
size_t hasher(const Sales_data& sd) {
return hash<string>() (sd.isbn());
}
//如果类中定义了operator==则只需重载哈希函数
bool eqOp(const Sales_data& lhs, const Sales_data& rhs) {
return lhs.isbn() == rhs.isbn();
}
int main() {
using SD_multiset = unordered_multiset<Sales_data, decltype(hahser)*, decltype(eqOp)*>; //C++11
//参数是桶大小、哈希函数指针和相等性判断运算符指针
SD_multiset bookstore(42, hasher, eqOp);
}
- 为什么能通过上述代码完成自定义类型作为无序容器的关键字呢?
- 模板参数
以下是unordered_multiset类的成员类型,其中key_equal
是typedef的第三个模板参数,该类创建的对象用于调用operator==比较两关键字是否相等。
member type | definition | notes |
---|---|---|
key_type |
the first template parameter (Key ) |
|
mapped_type |
the second template parameter (T ) |
|
value_type |
pair<const key_type,mapped_type> |
|
hasher |
the third template parameter (Hash ) |
defaults to: hash<key_type> |
key_equal |
the fourth template parameter (Pred ) |
defaults to: equal_to<key_type> |
allocator_type |
the fifth template parameter (Alloc ) |
defaults to: allocator<value_type> |
reference |
Alloc::reference |
|
const_reference |
Alloc::const_reference |
|
pointer |
Alloc::pointer |
for the default allocator: value_type* |
const_pointer |
Alloc::const_pointer |
for the default allocator: const value_type* |
iterator |
a forward iterator to value_type |
|
const_iterator |
a forward iterator to const value_type |
|
local_iterator |
a forward iterator to value_type |
|
const_local_iterator |
a forward iterator to const value_type |
|
size_type |
an unsigned integral type | usually the same as size_t |
difference_type |
a signed integral type | usually the same as ptrdiff_t |
- equal_to类模板的实现:
equal_to类在容器中创建的对象:key_eq
(其他无序容器均有)
- 对于unordered系列的构造函数,都有一个接受unsigned int参数的构造函数,表示哈希桶的大小,且不能被隐式类型转换
5 无序容器的管理操作
Buckets
-
Return number of buckets (public member function)
-
Return maximum number of buckets (public member function)
-
Return bucket size (public member type)
-
Locate element's bucket (public member function)
Hash policy
-
Return load factor (public member function)
-
Get or set maximum load factor (public member function )
-
Set number of buckets (public member function )
-
Request a capacity change (public member function)
cpp
#include <unordered_map>
#include <iostream>
using namespace std;
int main() {
unordered_map<int, int> mp(8);
cout << mp.bucket_count() << endl; //output: 8
mp.insert({ 1, 1 });
mp.insert({ 2, 1 });
mp.insert({ 3, 1 });
mp.insert({ 11, 1 });
for (int i = 0; i < 8; i++) {
cout << mp.bucket_size(i) << ' '; //output:0 0 0 0 1 0 2 1
}
}
6 总结
-
有序容器(
set multiset map multimap
)底层为红黑树,使用比较函数来比较关键字,从而将元素按顺序存储。默认情况下,比较操作是采用关键字类型的**<运算符(operator <)** -
无序容器(
unordered_set unordered_multiset unordered_map unordered_multimap
)底层是哈希桶,使用关键字类型的**==运算符(operator ==)*和一个hash<key_type>
类型的对象来计算哈希值从而组织对象(放入哪个哈希桶中)
,使用比较函数来比较关键字,从而将元素按顺序存储。默认情况下,比较操作是采用关键字类型的<运算符(operator <)** -
无序容器(
unordered_set unordered_multiset unordered_map unordered_multimap
)底层是哈希桶,使用关键字类型的**==运算符(operator ==)**和一个hash<key_type>
类型的对象来计算哈希值从而组织对象(放入哪个哈希桶中)