学习笔记:浅谈 STL
浅谈 STL
(从 basic_algorithm贺的,只是为了复习而已)
为什么 C++ 比 C 更受人欢迎呢?除了 C++ 的编译令人感到更舒适,C++ 的标准模板库(STL)也占了很重要的原因。当你还在用手手写快排、手写二叉堆,挑了半天挑不出毛病的时候,C++ 党一手 STL 轻松 AC,想不嫉妒都难。所以在学习 STL 的时候应认真体会 STL 语法及功能,提升自己在算法竞赛及程序设计中解题、码代码的能力。
1 vector
1.1 简介
vector 一词在英文中是向量的意思。
vector 为可变长数组(即动态数组),可以随时添加数值和删除数值。
注意
在局部区域中开 vector 是在堆空间开的
在局部区域开数组是在栈空间开的,而栈空间比较小,如果开了很大的数组就会爆栈。
所以,在局部区域中不能开大数组,但能开大 vector。
1.2 头文件
#include <vector>1.3 初始化
vector<int> a; //定义了一个名为a的一维数组,数组存储int类型数据
vector<double> b;//定义了一个名为b的一维数组,数组存储double类型数据
vector<node> c;//定义了一个名为c的一维数组,数组存储结构体类型数据,node是结构体类型
vector<int> v(n);//定义一个长度为n的数组,初始值默认为0,下标范围[0, n - 1]
vector<int> v(n, 1);//v[0]到v[n-1]所有的元素初始值均为1
//注意:指定数组长度之后(指定长度后的数组就相当于正常的数组了)
vector<int> a{1, 2, 3, 4, 5};//数组a中有五个元素,数组长度就为5
vector<int> a(n + 1, 0);
vector<int> b(a);//两个数组中的类型必须相同,a和b都是长度为n+1,初始值都为0的数组
vector<int> v[5];//定义可变长二维数组
//注意:行不可变(只有5行), 而列可变,可以在指定行添加元素
//第一维固定长度为5,第二维长度可以改变
vector<vector<int> > v;//定义一个行和列均可变的二维数组1.4 函数
| 函数 | 含义 | 
|---|---|
| front() | 返回第一个数据 $O(1)$ | 
| pop_back() | 删除最后一个数据 $O(1)$ | 
| push_back(x) | 在尾部加一个数据 $O(1)$ | 
| size() | 返回数据个数 $O(1)$ | 
| clear() | 清空容器 $O(n)$ | 
| resize(x, y) | 将数组大小改为 x, x 个空间赋值为 y,没有 y 默认为 0 | 
| insert(x, y) | 向迭代器 x 中插入一个数据 y $O(n)$ | 
| erase(x, y) | 删除 $[x, y)$ 中的所有数据 $O(n)$ | 
| begin() | 返回首元素迭代器 $O(1)$ | 
| end() | 返回末元素后一个位置的迭代器 $O(1)$ | 
| empty() | 判断容器是否为空 $O(1)$ | 
1.5 访问
可以直接和数组一样访问。
vector<int> a;
a.push_back(1);
cout << a[0] << endl;也可以采用迭代器访问。
vector <int> a;
a.push_back(1);
vector <int>::iterator tmp = a.begin();
cout << *tmp << endl;
for(tmp = a.begin() ; tmp != a.end() ; tmp ++)
        cout << *tmp << endl;也可以使用智能指针,但只能一次性遍历完整个数组。
vector<int> v;
v.push_back(12);
v.push_back(241);
for(auto val : v) 
    cout << val << " "; // 12 2412 stack
2.1 简介
栈是一种先进后出的容器。
2.2 头文件
#include <stack>2.3 初始化
stack<int> s;
stack<string> s;
stack<node> s; //node是结构体类型2.4 函数
| 函数 | 含义 | 
|---|---|
| push(x) | 将 x 入栈 $O(1)$ | 
| pop() | 将栈顶元素出栈 $O(1)$ | 
| top() | 返回栈顶元素 $O(1)$ | 
| empty() | 检测栈是否为空 $O(1)$ | 
| size() | 返回元素个数 $O(1)$ | 
2.5 访问
STL 中的栈仅支持读取栈顶元素,如果需要遍历则需要将所有元素出栈。
可以考虑用数组模拟栈,比 STL 的 stack 容器速度更快,且遍历元素更加方便。
3 queue
3.1 简介
队列是一种先进先出的数据结构。
3.2 头文件
#include <queue>3.3 初始化
queue<int> s;
queue<string> s;
queue<node> s; //node是结构体类型3.4 函数
| 函数 | 含义 | 
|---|---|
| front() | 返回队首元素 $O(1)$ | 
| back() | 返回队尾元素 $O(1)$ | 
| push(x) | 在尾部加入一个元素 $O(1)$ | 
| pop() | 将第一个元素出队 $O(1)$ | 
| size() | 返回队列元素个数 $O(1)$ | 
| empty() | 判断是否为空 $O(1)$ | 
3.5 访问
STL 中的队列仅支持访问队首和队尾的元素,如果要遍历队列中的元素则需要将所有元素出队。
可以考虑用数组模拟队列。
4 deque
4.1 简介
首尾都可以插入和删除元素的队列为双端队列。
4.2 头文件
#include <deque>4.3 初始化
deque<int> s;
deque<string> s;
deque<node> s; //node是结构体类型4.4 函数
| 函数 | 含义 | 
|---|---|
| front() | 返回队首元素 $O(1)$ | 
| back() | 返回队尾元素 $O(1)$ | 
| push_front(x) | 加入一个元素到队首 $O(1)$ | 
| push_back(x) | 加入一个元素到队尾 $O(1)$ | 
| pop_front() | 将第一个元素出队 $O(1)$ | 
| pop_back() | 将最后一个元素出队 $O(1)$ | 
| size() | 返回队列元素个数 $O(1)$ | 
| empty() | 判断是否为空 $O(1)$ | 
| clear() | 清空 deque | 
注意
deque 可以进行排序。
5 priority_queue
5.1 简介
优先队列在正常队列的基础上加入了优先级,保证每一次插入元素后队首元素都是优先级最大的。其底层实现是堆。
5.2 头文件
#include <queue>5.3 初始化
priority_queue<int> s;
priority_queue<string> s;
priority_queue<node> s; //node是结构体类型5.4 函数
| 函数 | 含义 | 
|---|---|
| top() | 返回队首元素 | 
| push(x) | 将元素入队 | 
| pop() | 队首元素出队 | 
| size() | 返回元素个数 | 
| empty() | 判断是否为空 | 
5.5 设置优先级
优先队列默认实现的是大根堆,如果需要实现小根堆的话可以考虑在入队和出队时对元素进行取反。
也可以设置参数。
priority_queue<int> q1; // 默认大根堆, 即每次取出的元素是队列中的最大值
priority_queue<int, vector<int>, less<int> > q2; // 大根堆, 每次取出的元素是队列中的最大值,同第一行
priority_queue<int, vector<int>, greater<int> > q3; // 小根堆, 每次取出的元素是队列中的最小值第二个参数:
vector<int> 是用来承载底层数据结构堆的容器,若优先队列中存放的是 double 型数据,就要填 vector<double>
总之存的是什么类型的数据,就相应的填写对应类型。同时也要改动第三个参数里面的对应类型。
第三个参数:
less<int> 表示数字大的优先级大,堆顶为最大的数字
greater<int> 表示数字小的优先级大,堆顶为最小的数字
int代表的是数据类型,也要填优先队列中存储的数据类型
也可以重载运算符。
struct node
{
    int x;
    bool operator<(const int &x)
    {
        return x > x.x;
    }
};
priority_queue <node> q;如果需要使用结构体类型也需要进行进行小于号重载。
特殊类型的优先级
排序规则:
默认先对 pair 的 first 进行降序排序,然后再对 second 降序排序
对 first 先排序,大的排在前面,如果 first 元素相同,再对 second 元素排序,保持大的在前面。
6 map/multimap
6.1 简介
STL 中的 map 并不是真正的地图,而是一种类似于函数的对应关系。内部实现为红黑树。
浅浅地举个栗子:
设函数 $f(x)=x+1(x\in \R)$,则对于任意一个 $x$ 都有一值 $x+1$ 与其对应。
在 map 中就变成了每一个键对应一个值。
6.2 头文件
#include <map>6.3 初始化
map<int, int> mp;6.4 函数
| 函数 | 含义 | 
|---|---|
| find(x) | 返回键为 x 的映射的迭代器 $O(\log n)$,数据不存在时返回末尾迭代器 | 
| erase(x) | 删除迭代器对应的键值 $O(1)$,或者根据键删除键值 $O(logn)$ | 
| size() | 返回映射的对数 $O(1)$ | 
| clear() | 删除所有映射 $O(n)$ | 
| insert(x) | 插入键值 x $O(\log n)$ | 
| empty() | 判断是否为空 $O(1)$ | 
| begin() | 返回第一个元素迭代器 $O(1)$ | 
| end() | 返回最后一个元素的后一个迭代器 $O(1)$ | 
| lower_bound(x) | 返回指向键值 $\geq$ x 的第一个元素 $O(\log n)$ | 
| upper_bound(x) | 返回指向键值 $>$ x 的第一个元素 $O(\log n)$ | 
| rbegin() | 返回指向 map 最后一个元素迭代器 $O(1)$ | 
| rend() | 返回指向 map 第一个原色前面的逆向迭代器 $O(1)$ | 
| count(x) | 检查元素 x 是否存在 $O(\log n)$ | 
6.5 添加
mp["yb"] = "akioi";
mp["yzj"] = "wc2023au";
mp.insert(make_pair("hhy","chickenbrother"));
mp.insert(pair<string, string>("yxf","大师"));
mp.insert({"tsq","tsqtsqtsq"});6.6 访问
map 可以使用下标访问,也可以使用迭代器访问。
mp["yzj"] = "wc2023au";
cout << mp["yzj"] << endl;
map<char,int>::iterator it = mp.find(`a`);
cout << it -> first << " " <<  it -> second << endl;智能指针:
for(auto i : mp)
    cout << i.first << " " << i.second << endl;//键,值正向遍历:
map<int, int> mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.begin();
while(it != mp.end())
{
    cout << it -> first << " " << it -> second << endl;
    it++;
}逆向遍历:
map<int,int> mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.rbegin();
while(it != mp.rend())
{
    cout << it -> first << " " << it -> second << endl;
    it++;
}注意:map 中键不能重复,但 multimap 可以。
7 set/multiset
7.1 简介
"set"一词的中文意思为“集合”,set 容器具有数学中集合的所有特性(除 multiset 允许存在重复元素之外),当插入元素时会自动从小到大排序。其内部实现为红黑树。
7.2 头文件
#include <set>7.3 初始化
set<int> s;
set<string> s;
set<node> s; //node是结构体类型7.4 函数
| 函数 | 含义 | 
|---|---|
| begin() | 返回第一个元素迭代器 $O(1)$ | 
| end() | 返回最后一个元素下一个地址的迭代器 $O(1)$ | 
| clear() | 删除所有元素 $O(n)$ | 
| empty() | 判断容器是否为空 $O(1)$ | 
| insert() | 插入一个元素 $O(\log n)$ | 
| size() | 返回元素个数 $O(1)$ | 
| lower_bound(x) | 返回值 $\geq$ x 的第一个元素 $O(\log n)$ | 
| upper_bound(x) | 返回值 $>$ x 的第一个元素 $O(\log n)$ | 
| find(x) | 返回 x 元素的迭代器 $O(\log n)$,数据不存在时返回末尾迭代器 | 
| rbegin() | 返回指向 map 最后一个元素迭代器 $O(1)$ | 
| rend() | 返回指向 map 第一个原色前面的逆向迭代器 $O(1)$ | 
| count(x) | 检查元素 x 是否存在 $O(\log n)$ | 
7.5 访问
可以使用迭代器访问。
for(set<int>::iterator it = s.begin(); it != s.end(); it++)
    cout << *it << " ";智能指针
for(auto i : s)
    cout << i << endl;访问最后一个元素
//第一种
cout << *s.rbegin() << endl;
//第二种
set<int>::iterator iter = s.end();
iter--;
cout << (*iter) << endl; //打印2;
//第三种
cout << *(--s.end()) << endl;7.6 重载运算符
set<int> s1; // 默认从小到大排序
set<int, greater<int> > s2; // 从大到小排序
//重载 < 运算符
struct cmp
{
    bool operator()(const int& u, const int& v)const
    {
       // return + 返回条件
       return u > v;
    }
};
set<int, cmp> s; 
for(int i = 1; i <= 10; i++)
    s.insert(i);
for(auto i : s)
    cout << i << " ";
// 10 9 8 7 6 5 4 3 2 1结构体
struct Point
{
    int x, y;
    bool operator<(const Point &p)const
    {
        // 按照点的横坐标从小到大排序,如果横坐标相同,纵坐标从小到大
        if(x == p.x)
            return y < p.y;
        return x < p.x;
    }
};
set<Point> s;
for(int i = 1; i <= 5; i++)
{
    int x, y;
    cin >> x >> y;
    s.insert({x, y});
}    
/* 输入
5 4
5 2
3 7
3 5
4 8
*/
for(auto i : s)
    cout << i.x << " " << i.y << endl;
/* 输出
3 5
3 7
4 8
5 2
5 4
*/注意:set 中值不能重复,但 multiset 可以。
8 pair
1 简介
pair 容器中只含有两个元素,可以看作是一种只有两个元素的结构体。
应用:
代替二元结构体
作为 map 键值进行插入
2 头文件
#include <utility>3 初始化
pair<string,int> p("yb",114514);//带初始值的
pair<string,int> p;//不带初始值的4 访问
//定义结构体数组
pair<int, int> p[20];
p[1] = {"yb",114514};
for(int i = 0; i < 20; i++)
{
    //和结构体类似,first代表第一个元素,second代表第二个元素
    cout << p[i].first << " " << p[i].second;
}9 string
string 是一个字符串类,和 char 字符串类似。
9.1 头文件
#include <string>9.2 特性
支持比较运算符
$+$ 代表拼接字符串
$>,<,\geq,\leq$ 代表根据字典序比较字符串的大小
9.3 读入
读入字符串,遇到空格结束。
string s;
cin >> s;读入字符串(包括空格),遇到回车结束。
string s;
getline(cin, s);注意:getline 会获取前一个输入的换行符吗,需要在前面添加读取换行符的语句,如 getchar()。
9.4 卡常
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);注意:在解锁之后不能混用 scanf, printf,getchar,getline。
9.5 函数
| 函数 | 含义 | 
|---|---|
| size() 和 length() | 返回字符个数 | 
| max_size() | 返回最多包含字符数 | 
| capacity() | 重新分配内存之前最多包含字符数 | 
| push_back() | 在末尾插入字符 | 
| insert(x, y) | 在 x 处插入字符 y | 
| append(x) | 在字符串结尾处添加字符串 x | 
| erase(x) | 删除字符串中 x 所指的字符 | 
| erase(x, y) | 删除 $[x, y)$ 上的所有字符 | 
| erase(x, y) | 删除从 x 开始的 y 个字符 | 
| replace(x, y, z) | 将当前字符串从 x 开始的 y 个字符替换为 z | 
| replace(x, y, z, t) | 将当前字符串从 x 开始的 y 个字符替换为 z 个字符 t | 
| tolower(s[i]) | 转换为小写 | 
| toupper(s[i]) | 转换为大写 | 
| substr(x, y) | 截取从 x 开始的 y 个字符 | 
| find(x, y) | 从 x 开始寻找 y | 
| rfind(x, y) | 从 x 开始反向寻找 y | 
10 bitset
10.1 简介
bitset 是标准库中的一个存储 0/1 的大小不可变容器,它类似数组,并且每一个元素只能是0或1,每个元素只用1bit空间。
10.2 头文件
#include <bitset>10.3 初始化
| 方法 | 含义 | 
|---|---|
| bitset < n > a | a 有 n 位,每一位都为 0 | 
| bitset < n >a(b) | a 是 unsigned long 型 u 的一个副本 | 
| bitset < n >a(s) | a 是 string 对象 s 中含有的位串的副本 | 
| bitset < n >a(s, pos, n) | a 是 s 中从位置 pos 开始的 n 个位的副本 | 
10.4 特性
可以进行位操作
bitset<4> foo (string("1001"));
bitset<4> bar (string("0011"));
cout << (foo ^= bar) << endl;// 1010 (foo对bar按位异或后赋值给foo)
cout << (foo &= bar) << endl;// 0010 (按位与后赋值给foo)
cout << (foo |= bar) << endl;// 0011 (按位或后赋值给foo)
cout << (foo <<= 2) << endl;// 1100 (左移2位,低位补0,有自身赋值)
cout << (foo >>= 1) << endl;// 0110 (右移1位,高位补0,有自身赋值)
out << (~bar) << endl;// 1100 (按位取反)
cout << (bar << 1) << endl;// 0110 (左移,不赋值)
cout << (bar >> 1) << endl;// 0001 (右移,不赋值)
cout << (foo == bar) << endl;// false (0110==0011为false)
cout << (foo != bar) << endl;// true (0110!=0011为true)
cout << (foo & bar) << endl;// 0010 (按位与,不赋值)
cout << (foo | bar) << endl;// 0111 (按位或,不赋值)
cout << (foo ^ bar) << endl;// 0101 (按位异或,不赋值)10.5 函数
| 函数 | 含义 | 
|---|---|
| any() | 是否存在值为 1 的二进制位 | 
| none() | 是否不存在值为 1 的二进制位 | 
| count() | 值为 1 的个数 | 
| size() | 二进制位的个数 | 
| test(x) | 测试 x 处是否为 1 | 
| a[x] | 返回 x 处的二进制位 | 
| set() | 将所有值变为 1 | 
| set(x) | 将 x 处变为 1 | 
| reset() | 将所有值变为 0 | 
| reset(x) | 将 x 处变为 0 | 
| flip() | 将所有值按位取反 | 
| flip(x) | 将 x 处按位取反 | 
11 tuple
11.1 简介
tuple 模板是 pair 的泛化,可以封装不同类型任意数量的对象。
可以把 tuple 理解为 pair 的扩展,tuple 可以声明二元组,也可以声明三元组。
tuple 可以等价为结构体使用。
11.2 头文件
#include <tuple>11.3 初始化
tuple<int, int, string> t1;11.4 操作
// 赋值
t1 = make_tuple(1, 1, "tsq");
// 创建的同时初始化
tuple<int, int, int, int> t2(1, 2, 3, 4);
// 可以使用pair对象构造tuple对象,但tuple对象必须是两个元素
auto p = make_pair("yb", 114514);
tuple<string, int> t3 {p}; //将pair对象赋给tuple对象
// 获取tuple对象t的第一个元素
int first = get<0>(t);
// 修改tuple对象t的第一个元素
get<0>(t) = 1;
// 获取元素个数
tuple<int, int, int> t(1, 2, 3);
cout << tuple_size<decltype(t)>::value << endl; // 3
// 获取对应元素的值
// 通过get<n>(obj)方法获取,n必须为数字不能是变量
tuple<int, int, int> t(1, 2, 3);
cout << get<0>(t) << endl; // 1
cout << get<1>(t) << endl; // 2
cout << get<2>(t) << endl; // 3
// 通过tie解包 获取元素值
// tie 可以让 tuple 变量中的三个值依次赋到 tie 中的三个变量中
int one, three;
string two; 
tuple<int, string, int> t(1, "tsq", 3);
tie(one, two, three) = t;
cout << one << two << three << endl; // 1tsq312 STL 函数
12.1 accumulate()
accumulate(beg, end, init)作用:$O(n)$ 对一个元素序列求和。
12.2 atoi()
atoi(const char *)作用:将字符串转换为 int 类型。
12.3 fill()
fill(beg,end,num)作用:$O(n)$ 对一个序列初始化赋值。
12.4 is_sorted()
is_sorted(beg,end)作用:判断序列是否有序。
12.5 iota()
iota(beg, end)作用:对序列递增赋值。
12.6 lower_bound + upper_bound()
//在a数组中查找第一个大于等于x的元素,返回该元素的地址
lower_bound(a + 1, a + n + 1, x);
//在a数组中查找第一个大于x的元素,返回该元素的地址
upper_bound(a + 1, a + n + 1, x);
//如果未找到,返回尾地址的下一个位置的地址作用:$O(\log n)$ 二分查找。
12.7 max_element + min_element()
//函数都是返回地址,需要加*取值
int maxx = *max_element(a + 1, a + n + 1);
int minn = *min_element(a + 1, a + n + 1);作用:$O(n)$ 查找最大最小值。
12.8 max() + min()
//找a,b的最大值和最小值
maxx = max(a, b);
minn = min(a, b);
//找a,b,c,d的最大值和最小值
maxx = max({a, b, c, d});
minn = min({a, b, c, d});作用:$O(1)$ 查找多个元素最大最小值。
12.9 minmax()
pair<int, int> t = minmax(1919810, 114514);
// t.first = 114514, t.second = 1919810作用:$O(1)$ 返回一个 pair 类型,第一个元素是 min(a, b) , 第二个元素是 max(a, b)。
12.10 random_shuffle()
vector<int> b(n);
iota(b.begin(), b.end(), 1);// 序列b递增赋值 1, 2, 3, 4,...
//对a数组随机重排 random_shuffle(a, a + n);
// C++11之后尽量使用shuffle
shuffle(b.begin(), b.end());作用:$O(n)$ 随机打乱序列
注意:在 C++14 中被弃用,在 C++17 中被废除,C++11 之后应尽量使用 shuffle 来代替。
12.11 reverse()
reverse(beg, end)作用:$O(n)$ 翻转序列。
12.12 sort()
sort(beg, end);
sort(beg, end, cmp);作用:$O(n\log n)$ 对序列排序。
12.13 stable_sort()
功能和sort()基本一样。
区别在于stable_sort()能够保证相等元素的相对位置,排序时不会改变相等元素的相对位置。
12.14 unique()
unique(beg, end);作用:$O(n)$ 消除重复元素,返回消除完重复元素的下一个位置的地址
如:a[] = {1,2,3,3,4};
unique之后a数组为{1,2,3,4,3}前面为无重复元素的数组,后面则是重复元素移到后面,返回a[4]位置的地址(不重复元素的尾后地址)
12.15 __gcd()
__gcd(a, b)作用:$O(\log n)$ 求 $a$ 和 $b$ 的最大公约数。
12.16 __lg()
__lg(a)- 求一个数二进制下最高位位于第几位(从第0位开始)(或二进制数下有几位)
- __lg(x)相当于返回 $\lfloor log_2x\rfloor$
- 复杂度 $O(1)$

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号