C++ Primer 第十一章 关联容器 重点解读

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中的元素

  1. 用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;
}
  1. 用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);   
}
  • 为什么能通过上述代码完成自定义类型作为无序容器的关键字呢?
  1. 模板参数

以下是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
  1. equal_to类模板的实现:

equal_to类在容器中创建的对象:key_eq(其他无序容器均有)

  1. 对于unordered系列的构造函数,都有一个接受unsigned int参数的构造函数,表示哈希桶的大小,且不能被隐式类型转换

5 无序容器的管理操作

Buckets
  • bucket_count

    Return number of buckets (public member function)

  • max_bucket_count

    Return maximum number of buckets (public member function)

  • bucket_size

    Return bucket size (public member type)

  • bucket

    Locate element's bucket (public member function)

Hash policy
  • load_factor

    Return load factor (public member function)

  • max_load_factor

    Get or set maximum load factor (public member function )

  • rehash

    Set number of buckets (public member function )

  • reserve

    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>类型的对象来计算哈希值从而组织对象(放入哪个哈希桶中)

相关推荐
尽蝶叙几秒前
C++:分苹果【排列组合】
开发语言·c++·算法
憧憬成为原神糕手26 分钟前
c++_list
开发语言·c++
zyh2005043027 分钟前
c++的decltype关键字
c++·decltype
idealzouhu28 分钟前
Java 并发编程 —— AQS 抽象队列同步器
java·开发语言
爱吃油淋鸡的莫何28 分钟前
Conda新建python虚拟环境问题
开发语言·python·conda
眰恦37436 分钟前
数据结构--第六章图
数据结构·算法
闲人编程36 分钟前
Python实现日志采集功能
开发语言·python·fluentd·filebeat·日志采集
Sol-itude42 分钟前
关于MATLAB计算3维图的向量夹角总是不正确的问题记录
开发语言·matlab·问题解决·向量
2401_862886781 小时前
蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,游卡,oppo,康冠科技,途游游戏,埃科光电25秋招内推
前端·c++·python·算法·游戏
sjsjs111 小时前
【数据结构-差分】力扣1589. 所有排列中的最大和
数据结构·算法·leetcode