STL
STL是提高C++编写效率的利器。
#include <vector>
vector
是一个变长数组,用多少开多少,支持随机访问,不支持在任意位置O(1)
插入。为了保证效率,元素的增删一般应该在末尾进行。vector
支持比较,比较按照字典序进行。
声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <vector>
using namespace std;
int main() { vector<int> a; vector<int> b[233];
struct Rec { int x, y; };
vector<Rec> c;
return 0; }
|
size/empty
a.size()
函数返回vector
的实际长度(包含的元素个数),a.empty()
函数返回一个bool
类型,判断vector
是否为空,若为空则返回True
,否则返回False
。二者的时间复杂度都是O(1)
。
1 2 3
| vector<int> a; a.size(); a.empty();
|
所有的STL容器都支持这两个方法,含义也相同。
clear
a.clear()
函数用于清空vector
里的元素。
1 2
| vector<int> a; a.clear();
|
迭代器
迭代器就像STL容器的指针,可以用*
操作符解除引用。
一个保存int的vector
的迭代器声明方法为:
1
| vector<int>::iterator it = a.begin();
|
vector
的迭代器是随机访问迭代器,可以把vector
的迭代器与一个整数相加减,其行为和指针的移动类似。可以把vector
的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。
begin/end
begin/end
相当于两个特殊的迭代器。
begin
函数返回指向vector
中第一个元素的迭代器。例如a
是一个非空的vector
,则*a.begin()
与a[0]
的作用相同。
1 2
| a.begin() == a[0]; a.end() == a[n];
|
所有的容器都可以视作一个前闭后开的结构,end
函数返回vector
的尾部,即第n 个元素再往后的“边界”。*a.end()
与a[n]
都是越界访问,其中n = a.size()
。
vector
的初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <vector>
using namespace std;
int main() { vector<int> a({1, 2, 3});
cout << a[0] << ' ' << *a.begin() << endl; cout << a[1] << ' ' << *(a.begin() + 1) << endl; cout << a[2] << ' ' << *(a.begin() + 2) << endl; for (int i = 0; i < 3; i ++) cout << a[i] << ' ';
return 0; }
|
输出
vector
的遍历
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
| #include <iostream> #include <vector>
using namespace std;
int main() { vector<int> a({1, 2, 3});
for (int i = 0; i < 3; i ++) cout << a[i] << ' '; cout << endl;
for (vector<int>::iterator i = a.begin(); i != a.end(); i ++) cout << *i << ' '; cout << endl;
for (auto i = a.begin(); i < a.end(); i ++) cout << *i << ' '; cout << endl;
for (auto x : a) cout << x << ' '; cout << endl;
return 0; }
|
输出
1 2 3 4
| 1 2 3 1 2 3 1 2 3 1 2 3
|
front/back
front
函数返回vector
的第一个元素,等价于*a.begin()
和a[0]
。
back
函数返回vector
的最后一个元素,等价于*--a.end()
和a[a.size() – 1]
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> #include <vector>
using namespace std;
int main() { vector<int> a({1, 2, 3});
cout << a.front() << ' ' << *a.begin() << ' ' << a[0] << endl; cout << a.back() << ' ' << *(a.end() - 1) << ' ' << a[a.size() - 1] << endl;
return 0; }
|
输出
push_back()
和pop_back()
a.push_back(x)
把元素x
插入到vector a
的尾部。时间复杂度为O(1)。
b.pop_back()
删除vector b
的最后一个元素。时间复杂度为O(1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <vector>
using namespace std;
int main() { vector<int> a({1, 2, 3});
a.push_back(4); for (auto x : a) cout << x << ' '; cout << endl;
a.pop_back();
for (auto y : a) cout << y << ' '; cout << endl;
return 0; }
|
输出
#include <queue>
——队列
头文件<queue>
主要包括循环队列queue
和优先队列priority_queue
两个容器。
声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <queue>
queue<int> q; queue<double> a;
struct Rec { int a, x, y; }; queue<Rec> b;
priority_queue<int> c; priority_queue<int,vector<int>, greater<int>> d; priority_queue<pair<int, int>>
sturct Rec { int a, b; bool operator < (const Rec& t) const { return a < t.a; } }; priority_queue<int> p;
|
循环队列queue
只能从队尾插入,只能从队头弹出。先进先出
1 2 3 4
| q.push(1); q.pop(); q.front(); q.back();
|
优先队列priority_queue
1 2 3
| q.push(1); q.top(); q.pop();
|
※ 队列、优先队列、栈没有clear()
函数,其他的STL容器都有。想要清空队列,可以重新定义一次q = queue<int>()
。
#include <stack>
——栈
先进后出。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> #include <stack>
using namespace std;
int main() { stack<int> stk;
stk.push(1); stk.top(); stk.pop(); }
|
#include <deque>
——双端队列
双端队列deque
是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是vector
和queue
的结合。与vector
相比,deque
在头部增删元素仅需要 O(1)的时间;与queue
相比,deque
像数组一样支持随机访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <deque> #include <iostream>
using namespace std;
int main() { deque<int> a; a.begin(); a.end(); a.front(); a.back();
a.push_back(1); a.push_front(1);
a[0];
a.pop_back(); a.pop_front(); a.clear(); }
|
#include <set>
头文件<set>
主要包括set
和multiset
两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set
和multiset
的内部实现是一棵红黑树,它们支持的函数基本相同。
声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <set> #include <iostream>
using namespace std;
int main() { set<int> a; multiset<int> b;
struct Rec { int x, y; bool operator< (const Rec& t) const { return x < t.x; } };
set<Rec> c;
multiset<double> s; }
|
size/empty/clear
与vector
类似。
迭代器
set
和multiset
的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持*
解除引用,仅支持++
和--
两个与算术相关的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <set> #include <iostream>
using namespace std;
int main() { set<int> a;
set<int>::iterator it = a.begin(); it ++; it --; ++ it; -- it; }
|
begin/end
返回集合的首/尾迭代器,时间复杂度均为O(1)。
s.begin()
是指向集合中最小元素的迭代器。
s.end()
是指向集合中最大元素的下一个位置的迭代器,--s.end()
是指向集合中最大元素的迭代器。
insert
s.insert()
是把一个元素x
插入到集合s
中,时间复杂度为O(logn)。
在set
中,若元素已存在,则不会重复插入该元素,对集合的状态无影响。
find
s.find(x)
在集合s
中查找等于x
的元素,并返回指向该元素的迭代器。若不存在则返回s.end()
,时间复杂度为O(logn)。
lower_bound/upper_bound
s.lower_bound(x)
查找大于等于x
的元素中最小的一个,并返回指向该元素的迭代器。
s.upper_bound(x)
查找大于x
的元素中最小的一个,并返回指向该元素的迭代器。
时间复杂度为O(logn)。
erase
若it
是一个迭代器,s.erase(it)
表示从s
中删除迭代器it
指向的元素,时间复杂度为O(logn)。
若x
是一个元素,s.erase(x)
表示从s
中删除所有等于x
的元素,时间复杂度为 O(k+logn),k是被删除的元素个数。
count
s.count()
返回集合s
中等于x
的元素个数,时间复杂度为 O(k+logn),k是元素x的个数。
如果s
是set
即元素不允许重复的有序集合,则若s
中存在元素x
,s.count()
返回1,否则返回0。
unordered_set/unordered_multiset
相当于无序的set/multiset
,不能进行二分操作即lower_bound/upper_bound
,但是其他操作的时间复杂度都是O(1),相比set/multiset
效率更高。
#include <map>
map
容器是一个键值对key-value
的映射,其内部实现是一棵以key
为关键码的红黑树。map
的key
和value
可以是任意类型,其中key
必须定义小于号运算符。
声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <map> #include <vector>
using namespace std;
int main() { map<int, int> a;
a[1] = 2;
a[100000000] = 3; cout << a[100000000] << endl;
map<string, int> b; b["Koku"] = 2;
map<string, vector<int>> c; c["Kuro"] = vector<int>({1, 2, 3}); }
|
size/empty/clear/begin/end
均与set
类似。
insert/erase
与set
类似,但其参数均是pair<key_type, value_type>
。
find
h.find(x)
在变量名为h
的map
中查找key
为x
的二元组。
[]
操作符
h[key]
返回key
映射的value
的引用,时间复杂度为O(logn)。
可以很方便地通过h[key]
来得到key
对应的value
,还可以对h[key]
进行赋值操作,改变key
对应的value
。
unordered_map
效率更高。(C++ 11支持)
#include <bitset>
进行位运算,是一个二进制的串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <bitset>
using namespace std;
int main() { bitset<1000> a; a[0] = 1;
a.set(3); a.reset(3);
a &= b; a != b;
a.count(); }
|
pair
pair
定义一个二元组,元素可以是任意类型。
1 2 3 4 5 6 7 8 9 10 11 12
| #include <iostream>
using namespace std;
int main() { pair<int, string> a; a = {3, "ditf"};
a.first(); a.second(); }
|
pair
支持双关键字比较,第一个关键字相同则比较第二个。
位运算与常用库函数
位运算
符号 |
运算 |
& |
与 |
丨 |
或 |
~ |
非 |
^ |
异或 |
>> |
右移 |
<< |
左移 |
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream>
using namespace std;
int main() { int a = 3, b = 6;
cout << (a ^ b) << endl;
return 0; }
|
常用操作:
- 求
x
的第k
位数字 x >> k & 1
例:输出13的二进制的每一位1 2 3 4 5 6 7 8 9 10 11 12
| #include <iostream>
using namespace std;
int main() { int a = 13; for (int i = 3; i >= 0; i --) cout << (a >> i & 1) << ' ';
return 0; }
|
输出
lowbit(x) = x & -x
.返回x
的最后一位1。若a = 11010
则返回01
,若a = 11000
则返回1000
。用补码求负数,-x
就是x
取反后加1。
常用库函数
reverse
翻转
时间复杂度为O(n)。
翻转一个vector
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream> #include <algorithm> #include <vector>
using namespace std;
int main() { vector<int> a({1, 2, 3, 4, 5});
reverse(a.begin(), a.end());
for (auto x : a) cout << x << ' ';
return 0; }
|
输出
翻转一个数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <iostream> #include <algorithm>
using namespace std;
int main() { int a[] = {1, 2, 3, 4, 5}; reverse(a, a + 5);
for (auto x : a) cout << x << ' ';
return 0; }
|
输出
unique
去重
返回去重(只去掉相邻的相同元素)之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。
1 1 2 3 3 4 -unique-> 1 2 3 4
返回值是新数组的下一个位置,即a.end()
该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。
将一个数组去重:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <algorithm> #include <vector>
using namespace std;
int main() { int a[] = {1, 1, 2, 2, 3, 3, 4};
int m = unique(a, a + 7) - a; cout << m << endl;
for (int i = 0; i < m; i ++) cout << a[i] << ' ';
return 0; }
|
输出
将一个vector
去重:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <algorithm> #include <vector>
using namespace std;
int main() { vector<int> a({1, 1, 2, 2, 3, 3, 4});
a.erase(unique(a.begin(), a.end()), a.end());
for (auto x : a) cout << x << ' ';
return 0; }
|
输出
random_shuffle
随机打乱
用法与reverse
相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <algorithm> #include <vector> #include <ctime>
using namespace std;
int main() { vector<int> a({1, 2, 3, 4, 5});
srand(time(0));
random_shuffle(a.begin(), a.end());
for (auto x : a) cout << x << ' ';
return 0; }
|
输出
sort
排序
对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义大小比较的函数,或者重载“小于号”运算符。
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 35 36 37 38 39
| #include <iostream> #include <algorithm> #include <vector> #include <ctime>
using namespace std;
bool cmp(int a, int b) { return a > b; }
int main() { vector<int> a({1, 2, 3, 4, 5});
srand(time(0));
random_shuffle(a.begin(), a.end()); for (auto x : a) cout << x << ' '; cout << endl;
sort(a.begin(), a.end()); for (auto x : a) cout << x << ' '; cout << endl;
sort(a.begin(), a.end(), greater<int>()); for (auto x : a) cout << x << ' '; cout << endl;
sort(a.begin(), a.end(), cmp); for (auto x : a) cout << x << ' ';
return 0; }
|
输出
1 2 3 4
| 3 2 1 4 5 1 2 3 4 5 5 4 3 2 1 5 4 3 2 1
|
sort
排序结构体
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 35 36 37 38 39 40
| #include <iostream> #include <algorithm> #include <vector> #include <ctime>
using namespace std;
struct Rec { int x, y; }a[5];
bool cmp(Rec a, Rec b) { return a.x < b.x; }
int main() { for (int i = 0; i < 5; i ++ ) { a[i].x = -i; a[i].y = i; }
for (int i = 0; i < 5; i ++ ) printf("(%d, %d)",a[i].x, a[i].y); cout << endl;
sort(a, a + 5, cmp); for (int i = 0; i < 5; i ++ ) printf("(%d, %d)",a[i].x, a[i].y); cout << endl;
return 0; }
|
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 35 36 37 38 39
| #include <iostream> #include <algorithm> #include <vector> #include <ctime>
using namespace std;
struct Rec { int x, y; bool operator< (const Rec &t) const { return x < t.x; } }a[5];
int main() { for (int i = 0; i < 5; i ++ ) { a[i].x = -i; a[i].y = i; }
for (int i = 0; i < 5; i ++ ) printf("(%d, %d)",a[i].x, a[i].y); cout << endl;
sort(a, a + 5); for (int i = 0; i < 5; i ++ ) printf("(%d, %d)",a[i].x, a[i].y); cout << endl;
return 0; }
|
输出
1 2
| (0, 0)(-1, 1)(-2, 2)(-3, 3)(-4, 4) (-4, 4)(-3, 3)(-2, 2)(-1, 1)(0, 0)
|
lower_bound/upper_bound
二分
lower_bound
的第三个参数传入一个元素x
,在两个迭代器(指针)指定的部分上执行二分查找,返回指向第一个大于等于x
的元素的位置的迭代器(指针)。
upper_bound
的用法和lower_bound
大致相同,唯一的区别是查找第一个大于x的元素。
两个迭代器(指针)指定的部分需要提前排好序。
数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <iostream> #include <algorithm>
using namespace std;
int main() { int a[] = {1, 2, 4, 5, 6};
int *p = lower_bound(a, a + 5, 4); int *q = upper_bound(a, a + 5, 4);
cout << *p << ' ' << *q << endl;
return 0; }
|
输出
vector
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream> #include <vector> #include <algorithm>
using namespace std;
int main() { vector<int> a({1, 2, 4, 5, 6});
int t = lower_bound(a.begin(), a.end(), 2) - a.begin(); int m = upper_bound(a.begin(), a.end(), 2) - a.begin();
cout << a[t] << ' ' << a[m] << endl;
return 0; }
|
输出