RoadMap**
- 新特性
- vector 向量/数组 (
#include <vecetor>
) - list 链表 (
#include <list>
) - deque 双端队列 (
#include <deque>
) - queue 队列 / stack 栈 (
#include <queue>/<stack>
) - 堆/优先队列 priority_queue
- set/multiset 集合 (
#include <set>
) - map/multimap 字典 (
#include <map>
)
新特性
emplace 系列函数
C++11 新特性,可以直接传入参数构造对象(见下面的例子说明),不需要拷贝或者移动内存,相比 push 系列拥有更好的性能;
除 array 外(array 不可变),所有标准容器都添加了相关函数,如 emplace、emplace_front、emplace_back、、emplace_hint(set/map) 等。
面试手写代码时可以用,平时网上 OJ 一般没有那么细的性能要求
1 | // 假设 Date 类型接收 3 个参数来构造对象 |
vector 向量/数组 (#include <vecetor>
)
下面详细列出 vector 所有常用过的接口(不是全部);其他容器大同小异,就不全列出来了
初始化
1
2
3
4
5vector<int> v1; // {}
vector<int> v2 = { 1,2,3,4 };
vector<int> v3(3, 10); // {10,10,10}
vector<int> v4(v2.begin() + 1, v2.end()); // {2,3,4}
vector<int> v5(v3); // {2,3,4}遍历
1
2
3
4
5
6for (auto it = v5.begin(); it != v5.end(); it++)
cout << *it << ' ';
for (auto i = 0; i < v3.size(); i++)
cout << v3[i] << ' ';
for (auto i : v4)
cout << i << ' ';逆序遍历
1
2for (auto it = v5.rbegin(); it != v5.rend(); it++)
cout << *it << ' ';容器长度
1
v2.size(); // len(v2)
获取第一个元素,最后一个元素
1
2
3v2.front() = 1; // v2[0] = 1
v2.back() -= v2.front(); // v2[-1] -= v2[0]
// 注意:这两个函数的返回的是元素的引用,可以直接对其修改判断为空
1
v1.empty(); // len(v1) == 0
尾插/弹出
1
2
3
4
5
6
7v1.push_back(3); // v1.append(3)
v1.push_back(2); // v1.append(2)
v1.push_back(1); // v1.append(1)
// last_val = v1.pop()
last_val = v1.back();
v1.pop_back(); // return void插入
1
2
3
4v1.insert(v1.begin() + 1, 0); // v1.insert(1, 0)
v1.insert(v1.begin() + 1, v2.begin(), v2.end());
// 这个挺厉害,好像 Python 都没有直接插入另一个数组的方法,
// 只能 `v1 = v1[:1] + v2 + v1[1:]`合并
1
v1.insert(v1.end(), v2.begin(), v2.end()) // v1 += v2
删除
1
2
3
4
5v1.erase(v1.begin() + 1); // del v1[1]
// erase() 有一个返回值,指向被删除元素下一个
// 删除区间
v1.erase(v1.begin() + 1, v1.begin() + 3); // del v1[1:3]清空
1
v1.clear()
其他操作
判断是否存在
1
2
3
4
5
6// 使用 find() 不需要引入头文件
// 注意: lower_bound() 要求容器有序,这里不适用
auto it = find(v2.begin(), v2.end(), val);
if(it != v2.end()) { // if val in v2:
//
}排序
1 | // 降序 |
交换 swap
1
2
3
4
5
6
7
8
9// vector.swap(v) 交换的是两个容器中的内容,而不是元素交换
vector<int> v1; // {}
vector<int> v2 = { 1,2,3,4 };
v1.swap(v2); // v1: {1,2,3,4}
// 交换两个元素
swap(v2[1, v2[2]])
// 这个 swap() 是内置函数,不清楚具体在哪个头文件
list 链表 (#include <list>
)
list 的接口与 vector 基本相同,多了 push_front/pop_front
此外不能随机存取(想一下链表和数组的区别);而 Python 中的 list 其实是数组。
- 头插
1 | list<int> l1 = { 1,2,3 }; |
deque 双端队列 (#include <deque>
)
把它当作支持 push_front/pop_front 的 vector 即可。
1 | deque<int> d1 = { 1,2,3 }; |
deque 是 queue/stack 的底层容器
queue 队列 / stack 栈 (#include <queue>/<stack>
)
这两个的内部容器都是 list deque,相当于对接口做了限制的 deque;实际中完全可以使用 deque 代替
- queue 开放的接口
- empty
- size
- front
- back
- push (push_back)
- pop (pop_front)
- stack 开放的接口
- empty
- size
- top (back)
- push (push_back)
- pop (pop_back)
1 | deque<int> d1 = { 1,2,3 }; |
注意: 它们都不能使用初始化列表,也不支持 range 构造;只要是支持对应接口的都可以作为它们的底层容器,比如 vector 不能做 queue 的容器,因为它不支持 pop_front(虽然不会报错);但它可以做 stack 的容器
堆/优先队列 priority_queue
priority_queue 被定义在 <queue>
中,但它的底层容器默认是 vector,而不是 deque
- priority_queue 支持的接口
- empty
- size
- top (front)
- push (push_back)
- pop (push_back)
默认构建的是最大堆/大顶堆
1 | vector<int> v1 = { 1,2,3 }; |
构建最小堆
1 |
|
记: 小于 -> 大顶堆;大于 -> 小顶堆
自定义优先级(效果同 greater)
1 | // 大于 -> 小顶堆 |
更复杂点的例子
1 | struct node { |
priority_queue 也不支持初始化列表,甚至还不支持
priority_queue<int> p1(v1);
,但又可以使用 range 构造(区别于 queue 和 stack);这就是 C++ 最讨厌的地方,不统一(我不认为存在技术上的问题)
C++ 还提供了 make_heap 方法(不是类)用于对数组类容器中的元素重排,但是不推荐使用
set/multiset 集合 (#include <set>
)
set 不支持重复元素,multiset (多重集)支持重复元素
内部实现是红黑树(平衡二叉搜索树),插入的同时也在排序(默认使用 less 比较器,即升序)
set 的查找效率高于 vector/deque/list 等容器
初始化
1
2
3
4
5set<int> s1;
set<int> s2{ 3,1,2 };
vector<int> v { 3,2,1,5,4 };
set<int> s3(v.begin(), v.end());遍历:默认遍历方式是中序遍历
1
2
3
4
5
6for (auto i : s3)
cout << i << ' ';
// 逆序
for (auto it = s3.rbegin(); it != s3.rend(); it++)
cout << *it << ' '; // {5,4,3,2,1}插入/删除
1
2s2.insert(4); // {1,2,3,4}
s2.erase(3); // {1,2,4}查找
set/multiset 支持三种查找方式: find, lower_bound, upper_bound;因为 set 是有序的,所以这三种方法其实都是二分查找
lower_bound 找目标下界(包含);upper_bound 找目标上界(不包含);find 内部调用的就是 lower_bound
注意: C++ 中的查找并不是返回 bool 值,而是返回一个指向目标的迭代器指针;如果没有找到,则指向 end() 指针。
1
2
3
4
5
6
7
8
9
10
11
12
13
14// set
auto it = s2.find(4);
if (it != s2.end()) {
//
}
// multiset
multiset<int> ms1{ 1,2,2,2,3,4 };
auto itp1 = ms1.find(2); // No.2 (迭代器指针是从 1 开始计数的)
auto itp2 = ms1.lower_bound(2); // No.2
cout << (itp1 == itp2) << endl; // True
auto itp3 = ms1.upper_bound(2); // No.5
cout << *itp3 << endl; // 3
map/multimap 字典 (#include <map>
)
底层也是红黑树,内部实现相当于一个 set<pair<key, value>>
初始化 + 遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35map<int, int> m1{ { 1,2 },{ 2,3 },{ 3,4 } };
map<int, int> m2(m1.begin(), m1.end());
map<int, int> m3(m2);
for (auto& i : m3)
cout << i.first << ": " << i.second << endl;
map<int, int>::iterator p = m1.begin();
for(; p != m1.end(); ++p)
cout << p->first << " " << p->second << endl;
int main222(){
int n,m;
cin >> n >> m;
vector<int> input(n*m);
map<int,int> m1;
map<int,int> m2;
int i,j,temp;
for(i=0; i<n; ++i)
for(j=0; j<m; ++j){
if((i*m+j) % 2 == 0){
cin >> temp;
input[i*m+j] = temp;
m1[temp]++;
}
else{
cin >> temp;
input[i*m+j] = temp;
m2[temp]++;
}
}
return 0;
}
int cnt1,max1,ncnt1,next1;
findMax(list1,cnt1,max1,ncnt1,next1);插入/更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20m3[1] = 3; // 更新
m3[4] = 5; // 插入
pair<map<int, int>::iterator, bool> ret;
ret = m3.insert(pair<int, int>(1, 4));
if (ret.second == false)
cout << "exist" << endl;
// hint 插入
auto it = m3.begin();
it = m3.insert(it, pair<int, int>(6, 7));
// 使用 hint 不一定能提高效率
// 很多编译器在实现时,只有当 hint 恰好是最终位置是才会使用 hint,否则直接忽略它
// 一般的使用场景是顺序插入一系列有序的数时,用返回值作为 hint
// ref: http://en.cppreference.com/w/cpp/container/set/emplace_hint
ret = m3.emplace(5, 6); // 插入
ret = m3.emplace(1, 4); // 未更新
it = m3.emplace_hint(it, 8, 9); // hint 插入
// emplace 内部调用的是 insert删除
1
2
3
4m3.erase(3); // 删除 by key
auto it = m3.find(7); // 删除 by iterator
if (it != m3.end())
m3.erase(it);查找
跟 set 类似,略