banner
NEWS LETTER

STL进阶 | 学习笔记

Scroll down

$STL$ 容器

$vector$ 变长数组

包含在 < vector > 头文件中

基本思想:倍增

系统为某一程序分配空间时,所用时间与空间大小无关,只与申请次数有关。因此 vector 为保证效率,需要尽可能减少申请次数。**vector 使用倍增的方式分配空间**

vector 在定义时,先开辟一定长度的空间,当元素个数大于当前数组长度时,开一个两倍于该长度的空间,再将原数组复制进新数组

由此可知,当开辟一个长度为 nvector 数组时,需进行 $log_2n$ 次开辟空间的操作,总复制次数为 n,平均插入每个数的时间复杂度是 $O(1)$

定义

1
2
3
4
5
6
7
8
9
10
11
vector<int> a;

vector<int> a(N);
// 定义一个长度为 N 的 vector 数组,初始化为0

vector<int> a(10, 3);
// 定义一个长度为 10 的 vector 数组,每个元素的值均为 3
vector<string> a(10, "wangk");

vector<int> a[10];
// 定义 10 个 vector 数组

操作函数

$O(1)$:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
a.size();
// 返回 a 数组中元素的个数
// 注意:size 函数为无符号类型,即使用 a.size() - 1 有 RE 风险

a.empty();
// 判断 a 数组是否为空

a.front();
// 返回 a 数组的第一个元素

a.back();
// 返回 a 数组的最后一个元素

a.push_back(x);
a.emplace_back(x);
// 向 a 数组最后插入一个元素 x

a.pop_back();
// 删除 a 数组最后一个元素

$O(n)$:

1
2
3
4
5
a.clear();
// 清空 a 数组,容量不变

a.erase(begin, end);
// 删除 [begin, end) 区域内元素

迭代器:

1
2
3
4
5
a.begin();
// 返回 a 数组第 0 个元素

a.end();
// 返回 a 数组最后一个元素的下一个位置
  • vector 还支持随机寻址。即可以使用
    a[2] 访问 a 数组第 3 个元素

遍历 vector 数组

1
2
3
4
5
6
7
8
9
for (int i = 0; i < a.size(); i ++) 
cout << a[i] << endl;

for (vector<int> :: iterator i = a.begin(); i != a.end(); i ++)
cout << *i << endl;

for (auto x : a)
cout << x << endl;

  • vector 还支持按照字典序比较两数组,直接使用运算符比较即可

$pair$ 二元组

定义:

1
2
3
4
5
pair<int, string> a;

a = make_pair(114, "Hello");

a = {514, "World"};

操作函数

1
2
3
4
5
a.first();
// 取 a 的第一个元素

a.second();
// 取 a 的第二个元素
  • 支持比较远算,与 vector 一样是按照字典序排序,以 first 为第一关键字,以 second 为第二关键字

ps: 一些奇技淫巧

如果有三个关键字需存储,我们仅需:

1
pair<int, pair<int, int>> a;

即在 p 中存入了三个整型变量


$string$ 字符串

定义

1
string a;

操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a.size(); || a.length();
// 返回字符串长度

a.empty();
// 返回字符串是否为空

a.clear();
// 清空字符串

a.substr(l ,n);
// 返回从 l 开始长度为 n 的字串
// 注:l 参数从 0 开始

a.c_str();
// 返回 a 存储字符串的起始地址,多用于输出

  • 字符串可以支持 + 运算,使用 a + b 即可将 a 和 b 两个字符串首位拼接在一起

$queue$ 队列

包含在 < queue > 头文件中

定义

1
2
3
queue<int> q;

q = queue<int>();

操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a.size();
// 返回队列中元素的个数,O(1)

a.empty();
// 判断队列是否为空

a.push();
// 向队尾插入一个元素

a.pop();
// 删除队头一个元素

a.front();
// 返回队头元素

a.back();
// 返回队尾元素

$deque$ 双端队列

常数和空间都极大,不建议使用

操作函数

$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
24
25
26
27
a.size();
// 返回队列中元素的个数

a.empty();
// 判断队列是否为空

a.clear();
// 清空队列

a.front();
// 返回队列最左侧元素

a.back();
// 返回队列最右侧元素

a.push_back(x);
// 在队列右侧插入元素 x

a.pop_back();
// 在队列右侧删除一个元素

a.push_front(x);
// 在队列左侧插入元素 x

a.pop_front();
// 在队列左侧删除一个元素

迭代器:

1
2
3
4
5
a.begin();
// 返回队列最左侧首元素

a.end();
// 返回队列最右侧元素的下一个位置
  • deque 还支持随机寻址。即可以使用
    a[2] 访问队列 a 从左往右数第 3 个元素

$priority_queue$ 优先队列 / 堆

具体的实现方式,可见[[2022.11.12 - 堆]]

定义

1
2
3
4
5
6
7
8
priority_queue<int> heap;
// 定义大根堆(从大到小排序,以最大值为根节点)

heap.push(-x);
// 在大根堆进行此操作,即可将 x 的绝对值按照从小到大排序(小根堆)

priority_queue<int, vector<int>, greater<int>> heap;
// 定义小根堆(从小到大排序,以最小值为根节点)

操作函数

1
2
3
4
5
6
7
8
9
10
11
heap.push();
// 向堆中插入一个元素

heap.pop();
// 删除堆顶一个元素

heap.top();
// 返回堆顶元素

heap.emplace(x);
// 创建一个堆,并将元素插入堆

$stack$ 栈

定义

1
stack<int> heap;

操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
heap.push();
// 向栈顶插入一个元素

heap.pop();
// 删除栈顶一个元素

heap.top();
// 返回栈顶元素

heap.size();
// 返回栈中元素的个数

a.empty();
// 判断栈是否为空

$set$ & $multiset$ 有序集合 & 有序多重集合

  • 基于平衡二叉树(红黑树)实现
    可动态维护有序序列,因此不支持随机寻址

  • set 中不会出现重复元素,重复插入的元素会被忽略。

  • multiset 允许重复元素的存在。

操作函数

$O(1)$

1
2
3
4
5
6
7
8
a.size();
// 返回集合元素个数

a.empty();
// 判断集合是否为空

a.clear();
// 清空集合

$O(log_2n)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
a.insert(x);
// 向集合中插入元素 x,返回 pair:{插入位置迭代器, 是否成功}

a.find(x);
// 在集合中查找元素 x,如果不存在则返回 a.end() 迭代器

a.count(x);
// 返回集合中元素 x 的数量

a.erase(x);
// 输入变量 - 删除所有 x
// 输入迭代器 - 删除迭代器所指向的 x

a.lower_bound(x);
// 返回大于等于 x 的最小元素的迭代器

a.upper_bound(x);
// 返回大于 x 的最小元素的迭代器

// 以上两个函数如果不存在这样的元素,则返回 a.end() 迭代器,如解引用迭代器可能会导致 RE

$map$ & $multimap$ 映射

包含在 < map > 头文件中

  • 基于平衡二叉树 (红黑树) 实现
  • 动态维护有序序列

定义

1
2
3
4
5
map<string, int> a;
// string 代表键 key, 不可重复
// int 代表值 val

a["wangk"] = 1;

操作函数

$O(1)$

1
2
3
4
5
6
7
8
a.size();
// 返回集合元素个数

a.empty();
// 判断集合是否为空

a.clear();
// 清空集合

$O(log_2n)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a.insert();
// 向映射中插入一个 pair

a.erase();
// 在映射中删除一个 pair,输入 pair 和迭代器均可

a.find();
// 查找映射

a.lower_bound(x);
// 返回大于等于 x 的最小元素的迭代器

a.upper_bound(x);
// 返回大于 x 的最小元素的迭代器

// 以上两个函数如果不存在这样的元素,则返回 a.end() 迭代器

访问与遍历

使用 a[x] 访问时,如果无法找到,则新建一个 $key$ 为 $x$ 的 $map$,$val$ 指向 $0$

1
2
for (auto x : a)
cout << x.first << " " << x.second << endl;

注意:在遍历中无法修改 $key$,只能修改 $value$

特殊使用

1
cout << a["wangk"] << endl;

输出 1,时间复杂度为 $O(log_2n)$

$unordered_set$ & $unordered_map$ 无序集合 / 映射

$unordered_multiset$ & $unordered_multimap$ 多重无序集合 / 映射

总体操作和 set map 等类似,操作的时间复杂度为 O(1),不支持 lower_bound()upper_bound() 以及 map 的特殊使用方法

注意:在初始化时会开 4 倍空间,警钟敲烂


bitset 容器

类似于 bool 类型数组,但 bitset 单个元素 0/1 只占 $1\ bit$ 空间(1byte = 8bit)

定义

1
2
bitset<10000> s;
// 定义长度为 10000 的 bitset

赋值

  • 定义同时直接赋值 bitset<100> s(string("101"));
  • 单个位置赋值 s[10] = 1; (即代表 $2^{10}$)
  • 整体赋值 s = 101;

运算 / 操作符

  • 取某一位 s[]
  • 支持比较 == !=
  • 支持位运算 >> << ~ & | ^

输出

  1. 转化为整型变量输出 printf("%d", s.to_ulong());

  2. 直接输出 cout << s; (带前导 $0$)

操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
s.count(); // 返回 1 的个数

s.any(); // 是否至少有一个 1

s.none(); // 是否全部为 0

s.set(); // 将所有位置初始化成 1

s.reset(); // 将所有位置初始化为 0

s.set(k, v); // 将第 k 位赋为 v

s.flip(); // 将所有位置取反

s.flip(k); // 将第 k 位取反

STL 函数

需引入 <alrorithm> 头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
find(begin, end, x);

count(begin, end, x);

revise(begin, end);
// 反转序列元素

random_shuffle(begin, end);
// 随机打乱元素

unique(begin, end);
// 对于数组元素去重,仅对排序好的序列有效

nth_element(bengin, begin + k, end, compare);
// 找出序列中第 n 大的元素,使其左边均为小于它的数,右边均为大于它的数。

lower_bound(begin, end, x); // 返回第一个大于等于 x 的数的指针
upper_bound(begin, end, x); // 返回第一个大于 x 的数的指针

next_permutation(begin, end);
prev_permutation(begin, end);
// 时间复杂度 O(n)
// 更改数列为原数列按字典序的上 / 下一个位置,可用于枚举全排列
// 函数返回值为 T/F,用于指示新序列是否生成

(以上 begin 代表头指针,end 代表尾指针)

值得注意的是,random_shuffle 自 C++14 起被弃用,C++17 起被移除。我们可以使用 shuffle 函数代替原来的 random_shuffle。使用方法为:

1
shuffle(v.begin(), v.end(), rng);

(最后一个参数传入的是使用的随机数生成器,一般情况使用以真随机数生成器 random_device 播种的梅森旋转伪随机数生成器 mt19937)。

Other Articles
cover
贪心 | 学习笔记
  • 22/12/21
  • 09:04
  • 基础算法
cover
哈希表 | 学习笔记
  • 22/11/13
  • 14:15
  • 数据结构
Article table of contents TOP
  1. 1. $STL$ 容器
    1. 1.1. $vector$ 变长数组
      1. 1.1.1. 基本思想:倍增
      2. 1.1.2. 定义
      3. 1.1.3. 操作函数
      4. 1.1.4. 遍历 vector 数组
    2. 1.2. $pair$ 二元组
      1. 1.2.1. 定义:
      2. 1.2.2. 操作函数
    3. 1.3. $string$ 字符串
      1. 1.3.1. 定义
      2. 1.3.2. 操作函数
    4. 1.4. $queue$ 队列
      1. 1.4.1. 定义
      2. 1.4.2. 操作函数
    5. 1.5. $deque$ 双端队列
      1. 1.5.1. 操作函数
    6. 1.6. $priority_queue$ 优先队列 / 堆
      1. 1.6.1. 定义
      2. 1.6.2. 操作函数
    7. 1.7. $stack$ 栈
      1. 1.7.1. 定义
      2. 1.7.2. 操作函数
    8. 1.8. $set$ & $multiset$ 有序集合 & 有序多重集合
      1. 1.8.1. 操作函数
    9. 1.9. $map$ & $multimap$ 映射
      1. 1.9.1. 定义
      2. 1.9.2. 操作函数
      3. 1.9.3. 访问与遍历
      4. 1.9.4. 特殊使用
    10. 1.10. $unordered_set$ & $unordered_map$ 无序集合 / 映射
    11. 1.11. $unordered_multiset$ & $unordered_multimap$ 多重无序集合 / 映射
    12. 1.12. bitset 容器
      1. 1.12.1. 定义
        1. 1.12.1.1. 赋值
        2. 1.12.1.2. 运算 / 操作符
        3. 1.12.1.3. 输出
        4. 1.12.1.4. 操作函数
  2. 2. STL 函数
Please enter keywords to search