STL快速讲解

C++ 标准模板库 (STL, Standard Template Library):包含一些常用数据结构与算法的模板的 C++ 软件库。其包含四个组件——算法 (Algorithms)、容器 (Containers)、仿函数 (Functors)、迭代器 (Iterators).

示例:

  • 算法:sort(a.begin(), a.end())
  • 容器:priority_queue<int> pque
  • 仿函数:greater<int>()
  • 迭代器:vector<int>::iterator it = a.begin()

1.1 内容总览

打勾的是本次将会详细讲解的,加粗的是算法竞赛中有必要学习的。

  • 顺序容器

    • array

    • vector

    • deque

    • forward_list

    • list

  • 关联容器

    • set
    • map
    • multiset
    • multimap
  • 无序关联容器

    • unordered_set
    • unordered_map
    • unordered_multiset
    • unordered_multimap
  • 容器适配器

    • stack
    • queue
    • priority_queue
    • flat_set
    • flat_map
    • flat_multiset
    • flat_multimap
  • 字符串

    • string (basic_string<char>)
  • 对与元组

    • pair
    • tuple

1.2 向量 vector

#include <vector>

连续的顺序的储存结构(和数组一样的类别),但是有长度可变的特性。

1.2.1 常用方法

构造

vector<类型> arr(长度, [初值])

时间复杂度: O ( n ) O(n) O(n)

常用的一维和二维数组构造示例,高维也是一样的(就是会有点长).

vector<int> arr;         // 构造int数组
vector<int> arr(100);    // 构造初始长100的int数组
vector<int> arr(100, 1); // 构造初始长100的int数组,初值为1

vector<vector<int>> mat(100, vector<int> ());       // 构造初始100行,不指定列数的二维数组
vector<vector<int>> mat(100, vector<int> (666, -1)) // 构造初始100行,初始666列的二维数组,初值为-1

构造二维数组的奇葩写法,千万别用:

vector<int> arr[100];         // 正确,构造初始100行,不指定列数的二维数组,可用于链式前向星存图
vector<int> arr[100](100, 1); // 语法错误!
vector<int> arr(100, 1)[100]; // 语法错误!
vector<int> arr[100] {{100, 1}, 这里省略98,{100, 1}}; // 正确但奇葩,使用列表初始化
尾接 & 尾删
  • .push_back(元素):在 vector 尾接一个元素,数组长度 + 1 +1 +1.
  • .pop_back():删除 vector 尾部的一个元素,数组长度 − 1 -1 1

时间复杂度:均摊 O ( 1 ) O(1) O(1)

// init: arr = []
arr.push_back(1);
// after: arr = [1]
arr.push_back(2);
// after: arr = [1, 2]
arr.pop_back();
// after: arr = [1]
arr.pop_back();
// after: arr = []
获取长度

.size()

获取当前 vector 的长度

时间复杂度: O ( 1 ) O(1) O(1)

for (int i = 0; i < arr.size(); i++)
    cout << a[i] << endl;
清空

.clear()

清空 vector

时间复杂度: O ( n ) O(n) O(n)

判空

.empty()

如果是空返回 true 反之返回 false.

时间复杂度: O ( 1 ) O(1) O(1)

改变长度

.resize(新长度, [默认值])

修改 vector 的长度

  • 如果是缩短,则删除多余的值
  • 如果是扩大,且指定了默认值,则新元素均为默认值 (旧元素不变)

时间复杂度: O ( n ) O(n) O(n)

1.2.2 适用情形

一般情况 vector 可以替换掉普通数组,除非该题卡常。

有些情况普通数组没法解决: n × m n\times m n×m 的矩阵, 1 ≤ n , m ≤ 1 0 6 1\leq n,m\leq 10^6 1n,m106 n × m ≤ 1 0 6 n\times m \leq 10^6 n×m106

  • 如果用普通数组 int mat[1000010][1000010],浪费内存,会导致 MLE。
  • 如果使用 vector<vector<int>> mat(n + 10, vector<int> (m + 10)),完美解决该问题。

另外,vector 的数据储存在堆空间中,不会爆栈。

1.2.3 注意事项

提前指定长度

如果长度已经确定,那么应当直接在构造函数指定长度,而不是一个一个 .push_back(). 因为 vector 额外内存耗尽后的重分配是有时间开销的,直接指定长度就不会出现重分配了。

// 优化前: 522ms
vector<int> a;
for (int i = 0; i < 1e8; i++)
    a.push_back(i);
// 优化后: 259ms
vector<int> a(1e8);
for (int i = 0; i < a.size(); i++)
    a[i] = i;
当心 size_t 溢出

vector 获取长度的方法 .size() 返回值类型为 size_t,通常 OJ 平台使用的是 32 位编译器(有些平台例如 cf 可选 64 位),那么该类型范围为 [ 0 , 2 32 ) [0,2^{32}) [0,232).

vector<int> a(65536);
long long a = a.size() * a.size(); // 直接溢出变成0了

Code

int n;
cin >> n;
vector<int> vv(n);//开辟 n 个空间,全部初始化为 0

vector<int> vec, e[110];//动态添加

//clear() 清空容器
vec.clear();
for (int i = 1; i <= n; i++)
	e[i].clear();//e->clear()是错的!

for (int i = 1; i <= n; i++) {
	vec.push_back(3);//末尾放入
	vec[i] = 2;//修改
}
vec.pop_back();//末尾删除

//insert,插入一个数到容器内某个位置,留给你们自行了解

//排序
sort(vec.begin(), vec.end());
for (int i = 1; i <= n; i++)
	sort(e[i].begin(), e[i].end());

//查找
int t = lower_bound(vec.begin(), vec.end(), 3) - vec.begin();
//找到第一个大于等于3的数,返回它在vec容器内的下标,如果不存在会返回vec.end()

//删除,需要保证指针链接性
for (auto it = vec.begin(); it != vec.end();) {
	if (*it == 4)it = vec.erase(it);//erase函数会返回删除该位的下一位的指针
	it++;
}

//遍历,常用两种
for (int i = 0; i < vec.size(); i++)
	cout << vec[i] << ' ';
cout << endl;
for (auto j : vec)
	cout << j << ' ';

//输出开头和末尾的数
cout << vec.front() << endl;
cout << vec.back();

1.3 栈 stack

#include <stack>

通过二次封装双端队列 (deque) 容器,实现先进后出的栈数据结构。

1.3.1 常用方法

作用用法示例
构造stack<类型> stkstack<int> stk;
进栈.push(元素)stk.push(1);
出栈.pop()stk.pop();
取栈顶.top()int a = stk.top();
查看大小 / 清空 / 判空

1.3.2 适用情形

如果不卡常的话,就可以直接用它而不需要手写栈了。

另外,vector 也可以当栈用,vector 的 .back() 取尾部元素,就相当于取栈顶,.push_back() 相当于进栈,.pop_back() 相当于出栈。

1.3.3 注意事项

不可访问内部元素!下面都是错误用法

for (int i = 0; i < stk.size(); i++)
    cout << stk[i] << endl;
for (auto ele : stk)
    cout << stk << endl;

1.4 队列 queue

#include <queue>

通过二次封装双端队列 (deque) 容器,实现先进先出的队列数据结构。

1.4.1 常用方法

作用用法示例
构造queue<类型> quequeue<int> que;
进队.push(元素)que.push(1);
出队.pop()que.pop();
取队首.front()int a = que.front();
取队尾.back()int a = que.back();
查看大小 / 清空 / 判空

1.4.2 适用情形

如果不卡常的话,就可以直接用它而不需要手写队列了。

1.4.3 注意事项

不可访问内部元素!下面都是错误用法

for (int i = 0; i < que.size(); i++)
    cout << que[i] << endl;
for (auto ele : que)
    cout << ele << endl;

1.5 优先队列 priority_queue

#include <queue>

提供常数时间的最大元素查找,对数时间的插入与提取,底层原理是二叉堆。

1.5.1 常用方法

构造

priority_queue<类型, 容器, 比较器> pque

  • 类型:要储存的数据类型
  • 容器:储存数据的底层容器,默认为 vector<类型>,竞赛中保持默认即可
  • 比较器:比较大小使用的比较器,默认为 less<类型>,可自定义
priority_queue<int> pque1;                            // 储存int的大顶堆
priority_queue<int, vector<int>, greater<int>> pque2; // 储存int的小顶堆

对于需要自定义比较器的情况,涉及一些初学时容易看迷糊的语法(重载小括号运算符 / lambda 表达式),在此就不展开讲了。如果想要了解,可以查阅 cppreference 中的代码示例。

其他
作用用法示例
进堆.push(元素)que.push(1);
出堆.pop()que.pop();
取堆顶.top()int a = que.top();
查看大小 / 判空

进出队复杂度 O ( log ⁡ n ) O(\log n) O(logn),取堆顶 O ( 1 ) O(1) O(1).

1.5.2 适用情形

持续维护元素的有序性:每次向队列插入大小不定的元素,或者每次从队列里取出大小最小/最大的元素,元素数量 n n n,插入操作数量 k k k.

  • 每次插入后进行快速排序: k ⋅ n log ⁡ n k\cdot n\log n knlogn
  • 使用优先队列维护: k ⋅ log ⁡ n k\cdot\log n klogn

1.5.3 注意事项

仅堆顶可读

只可访问堆顶,其他元素都无法读取到。下面是错误用法:

cout << pque[1] << endl;
所有元素不可写

堆中所有元素是不可修改的。下面是错误用法:

pque[1] = 2;
pque.top() = 1;

如果你恰好要修改的是堆顶元素,那么是可以完成的:

int tp = pque.top();
pque.pop();
pque.push(tp + 1);

Code

//自动排序内部数,小到大或大到小

	priority_queue<int, vector<int>, greater<int>> q1; 
	//greater是小根堆,用vector装载内部元素
	priority_queue<int, vector<int>, less<int>> q2; 
	//less是大根堆

	q1.push(3), q2.push(2);

	while (q1.size())
	{
		int t = q1.top();//小根堆所以取出的数是所有数内最小的
		q1.pop();
		cout << t << ' ';
	}

1.6 集合 set

#include <set>

提供对数时间的插入、删除、查找的集合数据结构。底层原理是红黑树。

集合三要素解释setmultisetunordered_set
确定性一个元素要么在集合中,要么不在
互异性一个元素仅可以在集合中出现一次❌(任意次)
无序性集合中的元素是没有顺序的❌(从小到大)❌(从小到大)

1.6.1 常用方法

构造

set<类型, 比较器> st

  • 类型:要储存的数据类型
  • 比较器:比较大小使用的比较器,默认为 less<类型>,可自定义
set<int> st1;               // 储存int的集合(从小到大)
set<int, greater<int>> st2; // 储存int的集合(从大到小)

对于需要自定义比较器的情况,涉及一些初学时容易看迷糊的语法(重载小括号运算符 / lambda 表达式),在此就不展开讲了。

遍历

可使用迭代器进行遍历:

for (set<int>::iterator it = st.begin(); it != st.end(); ++it)
    cout << *it << endl;

基于范围的循环(C++ 11):

for (auto &ele : st)
    cout << ele << endl;
其他
作用用法示例
插入元素.insert(元素)st.insert(1);
删除元素.erase(元素)st.erase(2);
查找元素.find(元素)auto it = st.find(1);
判断元素是否存在.count(元素)st.count(3);
查看大小 / 清空 / 判空

增删查时间复杂度均为 O ( log ⁡ n ) O(\log n) O(logn)

1.6.2 适用情形

  • 元素去重: [ 1 , 1 , 3 , 2 , 4 , 4 ] → [ 1 , 2 , 3 , 4 ] [1,1,3,2,4,4]\to[1,2,3,4] [1,1,3,2,4,4][1,2,3,4]
  • 维护顺序: [ 1 , 5 , 3 , 7 , 9 ] → [ 1 , 3 , 5 , 7 , 9 ] [1,5,3,7,9]\to[1,3,5,7,9] [1,5,3,7,9][1,3,5,7,9]
  • 元素是否出现过:元素大小 [ − 1 0 18 , 1 0 18 ] [-10^{18},10^{18}] [1018,1018],元素数量 1 0 6 10^6 106,vis 数组无法实现,通过 set 可以完成。

1.6.3 注意事项

不存在下标索引

set 虽说可遍历,但仅可使用迭代器进行遍历,它不存在下标这一概念,无法通过下标访问到数据。下面是错误用法:

cout << st[0] << endl;
元素只读

set 的迭代器取到的元素是只读的(因为是 const 迭代器),不可修改其值。如果要改,需要先 erase 再 insert. 下面是错误用法:

cout << *st.begin() << endl; // 正确。可读。
*st.begin() = 1;             // 错误!不可写!
不可用迭代器计算下标

set 的迭代器不能像 vector 一样相减得到下标。下面是错误用法:

auto it = st.find(2);      // 正确,返回2所在位置的迭代器。
int idx = it - st.begin(); // 错误!不可相减得到下标。

1.7 映射 map

#include <map>

提供对数时间的有序键值对结构。底层原理是红黑树。

映射:

1 → 2 2 → 2 3 → 1 4 → 5 ⋮ \begin{matrix} 1&\to&2\\ 2&\to&2\\ 3&\to&1\\ 4&\to&5\\ &\vdots \end{matrix} 12342215

性质解释mapmultimapunordered_map
互异性一个键仅可以在映射中出现一次❌(任意次)
无序性键是没有顺序的❌(从小到大)❌(从小到大)

1.7.1 常用方法

构造

map<键类型, 值类型, 比较器> mp

  • 键类型:要储存键的数据类型
  • 值类型:要储存值的数据类型
  • 比较器:键比较大小使用的比较器,默认为 less<类型>,可自定义
map<int, int> mp1;               // int->int 的映射(键从小到大)
map<int, int, greater<int>> st2; // int->int 的映射(键从大到小)

对于需要自定义比较器的情况,涉及一些初学时容易看迷糊的语法(重载小括号运算符 / lambda 表达式),在此就不展开讲了。

遍历

可使用迭代器进行遍历:

for (map<int, int>::iterator it = mp.begin(); it != mp.end(); ++it)
    cout << it->first << ' ' << it->second << endl;

基于范围的循环(C++ 11):

for (auto &pr : mp)
    cout << pr.first << ' ' << pr.second << endl;

结构化绑定 + 基于范围的循环(C++17):

for (auto &[key, val] : mp)
    cout << key << ' ' << val << endl;
其他
作用用法示例
增 / 改 / 查元素中括号mp[1] = 2;
查元素(返回迭代器).find(元素)auto it = mp.find(1);
删除元素.erase(元素)mp.erase(2);
判断元素是否存在.count(元素)mp.count(3);
查看大小 / 清空 / 判空

增删改查时间复杂度均为 O ( log ⁡ n ) O(\log n) O(logn)

1.7.2 适用情形

需要维护映射的场景可以使用:输入若干字符串,统计每种字符串的出现次数。(map<string, int> mp)

1.7.3 注意事项

中括号访问时默认值

如果使用中括号访问 map 时对应的键不存在,那么会新增这个键,并且值为默认值,因此中括号会影响键的存在性。

map<char, int> mp;
cout << mp.count('a') << endl; // 0
mp['a'];                       // 即使什么都没做,此时mp['a']=0已经插入了
cout << mp.count('a') << endl; // 1
cout << mp['a'] << endl;       // 0
不可用迭代器计算下标

map 的迭代器不能像 vector 一样相减得到下标。下面是错误用法:

auto it = mp.find('a');      // 正确,返回2所在位置的迭代器。
int idx = it - mp.begin();   // 错误!不可相减得到下标。

Code

// 红黑树结构,可以自动排序去重
set<int> se;

// 关键字 关键值
map<int, int> mp; // map相当于拥有了关键值的set

// 插入

se.insert(3); // 插入一个 3 关键字

mp.insert({1, 2}); // 插入一个 1 关键字,并给这个关键字的关键值赋值 2
mp.insert({1, 1}); // 因为 1 关键字已经存在,所以把关键值修改成 1

// mp.insert({ 1,2 }) 等价于
mp[1] = 2;

mp[1] += 2; // 1 关键字的关键值 + 2

// 遍历

for (auto j : se)
    cout << j << endl;

for (auto j : mp)
    cout << j.first << ' ' << j.second << endl; // 两个值

for (auto it = mp.begin(); it != mp.end(); it++)
    cout << (*it).first << ' ' << (*it).second << endl;
// 无括号会报错

for (auto it = se.rbegin(); it != se.rend(); it++) // 反向遍历
    cout << *it << endl;

// 删除

mp.erase(3);
se.erase(3); // 删除关键字为 3 的元素

// 如果是遍历删除 map 内特定的第 K 个数,跟 vector 删除一样要注意指针衔接
int k = 0;
for (auto it = mp.begin(); it != mp.end(); k++)
{
    if (k == K)
        it = mp.erase(it);
    else
        it++;
}

int t = mp.count(3); // 返回指定元素出现的次数
t = mp.size();       // 返回map中元素的个数

// 查找

auto j = mp.find(3); // 查找 3 关键字在 map 内的下标
// 如果不存在则 j == mp.end()
if (j != mp.end())
    cout << (*j).first;

auto g = mp.lower_bound(3); // 查找 map 内第一个大于等于 3 的数的下标
// 如果不存在则 g == mp.end()
if (g != mp.end())
    cout << (*g).first;

1.8 字符串 string

#include <string>

顾名思义,就是储存字符串的。

1.8.1 常用方法

构造

构造函数:string(长度, 初值)

string s1;           // 构造字符串,为空
string s2 = "awa!";  // 构造字符串,并赋值awa!
string s3(10, '6');  // 构造字符串,通过构造函数构造为6666666666
其他
作用用法示例
是否相同==if (s1 == s2) ...
字符串连接+string s = s1 + s2;
尾接字符串+=s += "awa";
取子串.substr(起始下标, 子串长度)string sub = s.substr(2, 10);
查找字符串.find(字符串, 起始下标)int pos = s.find("awa");
插入字符.insert()s.insert(0,"heo"); s.insert(4,"world",2);
替换字符.replace(起始下标, 子串长度)s = s.replace(s.find(“c”),2,“#”); //从第一个 a 位置开始的两个字符替换成#
数值与字符串互转(C++11)

最常用的是 stoll 和 stod
stoll 可以兼容 stoi,stol stod 可以兼容 stof。

目的函数
int / long long / float / double / long doublestringto_string()
stringintstoi()
stringlong longstoll()
stringfloatstof()
stringdoublestod()
stringlong doublestold()

1.8.2 注意事项

尾接字符串一定要用 +=

string 的 += 运算符,将会在原字符串原地尾接字符串。而 + 了再 = 赋值,会先生成一个临时变量,在复制给 string.

通常字符串长度可以很长,如果使用 + 字符串很容易就 TLE 了。

//多用+=
// 优化前: 15139ms
string s;
for (int i = 0; i < 5e5; i++)
    s = s + "a";

// 优化后: < 1ms (计时器显示0)
string s;
for (int i = 0; i < 5e5; i++)
    s += "a";
.substr() 方法的奇葩参数

一定要注意,C++ string 的取子串的第一个参数是子串起点下标,第二个参数是子串长度

第二个参数不是子串终点!不是子串终点!要与 java 等其他语言区分开来。

若 pos 的值超过了 string 的大小,则 substr 函数会抛出一个 out_of_range 异常;

若 pos+n 的值超过了 string 的大小,则 substr 会调整 n 的值,只拷贝到 string 的末尾

.find() 方法的复杂度

该方法实现为暴力实现,时间复杂度为 O ( n 2 ) O(n^2) O(n2).

不要幻想 STL 内置了个 O ( n ) O(n) O(n) 的 KMP 算法

Code

string a, b;
	a.clear(), b.clear();//清空
	cin >> a >> b;//输入

	a.push_back('b');//最末尾添加一个字符
	a.pop_back();//删除最末尾一个字符

	a += b;//b字符串接到a后部,无 a -= b 语法

	//遍历
	for (int i = 0; i < a.size(); i++) {
		cout << a[i] << ' ';//输出每个位置字符
		a[i] = 'b';//修改
	}

	//find,找到第一个 x 字符的下标
	int t = a.find('a');

	//substr,取子串函数
	string g = a.substr(0, 3);//0 下标开始包括此位置往后取 3 个字符
	g = a.substr(3, 3);
	g = a.substr(3);//3下标开始取到结束

1.9 二元组 pair

#include <utility>

顾名思义,就是储存二元组的。

1.9.1 常用方法

构造

pair<第一个值类型, 第二个值类型> pr

  • 第一个值类型:要储存的第一个值的数据类型
  • 第二个值类型:要储存的第二个值的数据类型
pair<int, int> p1;
pair<int, long long> p2;
pair<char, int> p3;
// ...
赋值

老式

pair<int, char> pr = make_pair(1, 'a');

列表构造 C++11

pair<int, char> pr = {1, 'a'};
取值

直接取值

  • 取第一个值:.first
  • 取第二个值:.second
pair<int, char> pr = {1, 'a'};
int awa = pr.first;
char bwb = pr.second;

结构化绑定 C++17

pair<int, char> pr = {1, 'a'};
auto &[awa, bwb] = pr;
判同

直接用 == 运算符

pair<int, int> p1 = {1, 2};
pair<int, int> p2 = {1, 3};
if (p1 == p2) { ... } // false

1.9.2 适用场景

所有需要二元组的场景均可使用,效率和自己定义结构体差不多。

Code

//一个变量携带两个值
pair<int, int> pii;

cin >> pii.first >> pii.second;
	
cout << pii.first << ' ' << pii.second;

//通常作为工具与其他容器结合使用

2 迭代器简介

2.1 迭代器是什么?

对于一个 vector,我们可以用下标遍历:

for (int i = 0; i < a.size(); i++)
    cout << a[i] << endl;

我们同时也可以用迭代器来遍历:

for (vector<int>::iterator it = a.begin(); it != a.end(); ++it)
    cout << *it << endl;
  • a.begin() 是一个迭代器,指向的是第一个元素
  • a.end() 是一个迭代器,指向的是最后一个元素再后面一位
  • 上述迭代器具有自增运算符,自增则迭代器向下一个元素移动
  • 迭代器与指针相似,如果对它使用解引用运算符,即 *it,就能取到对应值了

2.2 为何需要迭代器?

很多数据结构并不是线性的(例如红黑树),对于非线性数据结构,下标是无意义的。无法使用下标来遍历整个数据结构。

迭代器的作用就是定义某个数据结构的遍历方式,通过迭代器的增减,代表遍历到的位置,通过迭代器便能成功遍历非线性结构了。

例如,set 的实现是红黑树,我们是没法用下标来访问元素的。但是通过迭代器,我们就能遍历 set 中的元素了:

for (set<int>::iterator it = st.begin(); it != st.end(); ++it)
    cout << *it << endl;

2.3 迭代器用法

对于 vector 容器,它的迭代器功能比较完整,以它举例:

  • .begin():头迭代器
  • .end():尾迭代器
  • .rbegin():反向头迭代器
  • .rend():反向尾迭代器
  • 迭代器 + 整型:将迭代器向后移动
  • 迭代器 - 整型:将迭代器向前移动
  • 迭代器 ++:将迭代器向后移动 1 位
  • 迭代器 --:将迭代器向前移动 1 位
  • 迭代器 - 迭代器:两个迭代器的距离
  • prev(it):返回 it 的前一个迭代器
  • next(it):返回 it 的后一个迭代器

对于其他容器,由于其结构特性,上面的功能不一定都有(例如 set 的迭代器是不能相减求距离的)

2.4 常见问题

.end().rend() 指向的位置是无意义的值

对于一个长度为 10 的数组:for (int i = 0; i < 10; i++),第 10 位是不可访问的

对于一个长度为 10 的容器:for (auto it = a.begin(); it != a.end(); ++it),.end 是不可访问的

不同容器的迭代器功能可能不一样

迭代器细化的话有正向、反向、双向,每个容器的迭代器支持的运算符也可能不同,因此不同容器的迭代器细节很有可能是不一样的。

删除操作时需要警惕

为什么 3 没删掉?

vector<int> a{1, 2, 3, 4};
for (auto it = a.begin(); it != a.end(); ++it)
    if (*it == 2 || *it == 3)
        a.erase(it);
// a = [1, 3, 4]

为啥 RE 了?

vector<int> a{1, 2, 3, 4};
for (auto it = a.begin(); it != a.end(); ++it)
    if (*it == 4)
        a.erase(it);
建议:如无必要,别用迭代器操作容器。(遍历与访问没关系)

补充

22、STL
1.优先队列 priority_queue
empty()如果队列为空返回真

pop()删除对顶元素

push()加入一个元素

size()返回优先队列中拥有的元素个数

top()返回优先队列队顶元素

在默认的优先队列中,优先级高的先出队。在默认的int型中先出队的为较大的数。

priority_queueq1;//大的先出对
priority_queue<int,vector,greater >q2; //小的先出队
自定义比较函数:
struct cmp
{
bool operator ()(int x, int y)
{
return x > y; // x小的优先级高
//也可以写成其他方式,如: return p[x] > p[y];表示p[i]小的优先级高
}
};
priority_queue<int, vector, cmp>q;//定义方法
//其中,第二个参数为容器类型。第三个参数为比较函数。
结构体排序:
struct node
{
int x, y;
friend bool operator < (node a, node b)
{
return a.x > b.x; //结构体中,x小的优先级高
}
};
priority_queueq;//定义方法
//在该结构中,y为值, x为优先级。
//通过自定义operator<操作符来比较元素中的优先级。
//在重载”<”时,最好不要重载”>”,可能会发生编译错误
2.set 和 multiset
set和 multiset用法一样,就是 multiset允许重复元素。

元素放入容器时,会按照一定的排序法则自动排序,默认是按照 less<>排序规则来排序。不

能修改容器里面的元素值,只能插入和删除。

自定义 int排序函数:(默认的是从小到大的,下面这个从大到小)

struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs>rhs;}
};//这里有个逗号的,注意
multiset<int,classcomp> fifth;
// class as Compare
上面这样就定义成了从大到小排列了。
结构体自定义排序函数:
(定义 set或者 multiset的时候定义了排序函数,定义迭代器时一样带上排序函数)
struct Node
{
int x,y;
};
struct classcomp//先按照 x从小到大排序,x相同则按照y从大到小排序
{
bool operator()(const Node &a,const Node &b)const
{
if(a.x!=b.x)return a.x<b.x;
else return a.y>b.y;
}
}; //注意这里有个逗号
multiset<Node,classcomp>mt;
multiset<Node,classcomp>::iterator it;
Multiset //该函数是set的多重集合形式可保存eg:1 2 2 3 3 4 5
multimap<int, int>a;
int main(){
a.insert(pair<int,int>(1, 1));
a.insert(pair<int, int>(2, 1));
a.insert(pair<int, int>(1, 2));
for (multimap<int, int>::iterator it = a.begin(); it != a.end(); it++)
cout << (*it).first<<" "<<(*it).second << endl;
return 0;
}
主要函数:

begin()返回指向第一个元素的迭代器

clear()清除所有元素

count()返回某个值元素的个数

empty()如果集合为空,返回 (true)

end()返回指向最后一个元素的迭代器

erase()删除集合中的元素 (参数是一个元素值,或者迭代器 )

find()返回一个指向被查找到元素的迭代器

insert()在集合中插入元素

size()集合中元素的数目

lower_bound()返回指向大于(或等于)某值的第一个元素的迭代器

upper_bound()返回大于某个值元素的迭代器

equal_range()返回集合中与给定值相等的上下限的两个迭代器

(注意对于 multiset删除操作之间删除值会把所以这个值的都删掉,删除一个要用迭代器 )

3.lower_bound and upper_bound
lower_bound() //在first和last中的前闭后开区间进行二分查找(故要先sort()),返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置(且a[last]不存在)

Eg: Pos= lower_bound(a,a+N,val)-a;

upper_bound() //也是前闭后开区间,且返回第一个大于val的位置,如果所有元素都小于val,则返回last的位置。

Eg: Pos= upper_bound (a,a+N,val)-a;

4.next_permutation and prev_permutation
next_permutation() //排列组合使用,eg:1 2 3 4 =》1 2 4 3 =》1 3 2 4 =》…

prev_permutation() //和上面的相反,返回上一项结果

Eg: next_permutation(a,a+N); //执行成功返回1,否则返回0

5.reverse()
//可以对数组,字符串,vector等进行翻转操作123=》321

Eg: reverse(a,a+N);

6.Map
map<int, char>m;
map<int, char>::iterator it;
m[8] = ‘a’;
m[6] = ‘b’;
m[11] = ‘c’;
it = m.begin();
cout << it->first << " " << it->second << endl; //输出6 b
it = m.end(); it–;
cout << it->first << " " << it->second << endl; //输出11 c
//map按照平衡二叉树原理类似,begin()为最左下角(min)
//end()为最右下角(max)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值