之前为了准备华为机试,系统地学习了一遍STL容器,现整理笔记内容出来供大家日常使用。STL容器也是面试高频考点,平时我也会使用零碎的时间来回顾这些知识点,常学常新~
vector(动态数组)
首先vector是什么?
vector就是一个是动态数组。与数组有相似的地方,可以和数组一样用下标的方式访问,可以和数组一样的方式进行初始化。不同的地方在于vector是不定长的,运行期间在堆空间分配内存,但是数组是固定长度的,在编译期间栈空间分配内存。
特点
- 动态数组,支持自动扩容,底层内存分配在堆上。
- 元素通过下标访问,与普通数组类似,但大小不固定。
定义方式
- 定义空向量
#include <vector>
vector<int> a;
//使用自定义结构体
struct node {
string name;
int score;
};
vector<node> n;
- 定义并初始化
vector<int> b = {1, 2, 3, 4, 5};
vector<int> c(10, 1); // 10 个元素,值为 1
vector<int> d(10); // 10 个元素,值为默认值 0
- 从已有向量或数组创建
vector<int> e(b); // 拷贝构造,写vector<int> e = b也可以
vector<int> f(b.begin(), b.end()); // 范围初始化
int arr[5] = {1, 2, 3, 4, 5};
vector<int> g(arr, arr + 5); // 从数组创建
常用操作
访问和修改元素
vector<int> vec = {1, 2, 3};
cout << vec[0]; // 输出 1
vec[1] = 10; // 修改元素值
遍历向量
- 使用下标遍历
vector<int> a(10, 1);
for (int i = 0; i < a.size(); i++) {
cout << a[i] << " ";
}
- 使用迭代器的方式进行遍历
vector<int>::iterator it; // it就是迭代器
for (it = a.begin(); it != a.end(); ++it) { //++it == it=it+1; it==a.end();
cout << *it << " ";
}
- 使用范围循环(C++11)
for (auto &val : vec) {
cout << val << " ";
}
常用函数
对于一个函数也可以在本地IDE查看源码,查看方法体、参数、返回值
vec.size(); // 获取元素个数
vec.empty(); // 检查是否为空
vec.front(); // 返回第一个元素,时间复杂度O(1)
vec.back(); // 返回最后一个元素,时间复杂度是O(1)
vec.push_back(10); // 末尾添加元素
vec.pop_back(); // 删除末尾元素
vec.clear(); // 清空向量
vec.insert(vec.begin() + 1, 20); // 指定位置插入
vec.erase(vec.begin() + 1); // 指定位置删除
vec.resize(5, 0); // 改变大小,多出的元素初始化为 0
sort(vec.begin(), vec.end()); // 排序
vec.front()
返回第一个数据 时间复杂度O(1)vec.back()
返回最后一个数据 时间复杂度是O(1)push_back(int value)
在vector最后面插入一个新的元素
vector<int>a(10,1);
a.push_back(2);
pop_back()
删除从向量最后一个元素,注意没有参数。如果说向量已经为空,再使用pop_back()会出错,需要进行判断
vector<int> a(10);
a.push_back(2); // 删除元素
if (!a.empty()) {// 向量非空
a.pop_back();
}
vec.empty()
查看向量是否为空;若向量为空,则返回true; 不为空,返回false
vector<int> a(10);
a.push_back(2);
if (a.empty()) { // empty()判断函数
cout << "数组为空";
} else {
cout << "数组不为空";
}
vec.size()
返回向量长度
vector<int> vec;
vec.size(); // 长度函数
-
vec.begin()
返回首元素的迭代器,通俗地说是返回首元素的地址 -
vec.end()
返回最后一个元素的后一个位置的迭代器(地址) -
sort()
排序函数
#include <algorithm>
sort(c.begin(), c.end());
//如果要对指定区间进行排序,可以对sort()里面的参数进行改动
vector<int> a(n + 1);
sort(a.begin() + 1, a.end()); // 对[1, n]区间进行从小到大排序
其他函数
c.clear()
用于清空向量中的全部元素a.erase(iterator first, iterator last)
删除自定义区间内的值
//删除容器中从第二个元素开始(索引为 1)到末尾的所有元素。
a.erase(a.begin()+1, a.end())
c.resize(n, 1)
改变向量大小为 n,若原来向量大于 n,则进行裁剪;若原来向量小于 n,多的部分数值赋为1,如果没有默认赋值为0 。c.insert(it, x)
向任意迭代器it插入一个元素x ,O ( N )
c.insert(c.begin() + 2,-1) // 将-1插入c[2]的位置
queue(队列)
队列的首元素是队头,最后的元素是队尾。
在队列中,只能访问它的队头和队尾,使用头指针访问队头元素,使用尾指针访问队尾元素,队列使用这两个指针完成队列的增删改查。
特点
- 先进先出,只能操作队头和队尾。
定义
//头文件
#include <queue>
//定义一个queue变量
queue<int> q;
注意:初始时,队列为空
访问队列元素
queue在概念中已经讲过,只能通过头指针和尾指针访问队列的元素,不能通过下标的方式进行访问
常用函数
- 入队和出队
push(/*元素*/)
入队的操作和vector很相似,都是在尾部插入一个元素,但是和vector函数名不同pop()
删除一个元素,这里和vector不同,队列是从头部进行删除元素
q.push(1); // 入队
q.pop(); // 出队
- 访问队头和队尾
int value1 = q.front(); // 返回队头元素
int value2 = q.back(); // 返回队尾元素
- 检查是否为空
empty()
用来检测队列是否为空的,为空就返回true
if (q.empty()) {
cout << "队列为空";
}
- 清空队列
while (!q.empty()) {
q.pop();
}
size()
返回队列长度
set(集合)
set容器的元素不会重复,并且set容器里的元素自动从小到大排序。六个字总结:不重复且有序
特点
- 不重复元素,自动排序。
定义
#include <set>
set<int> s;
常用操作
遍历集合
- 迭代器遍历
set<int>::iterator it;
for (it = s.begin(); it != s.end(); it++) { // 观察上下代码
cout << *it;
}
- 智能指针遍历
for (auto i : s) {
cout << i;
}
访问单个元素
- 访问首元素
int value = *(s.begin());
- 访问尾元素的值
int value = *(s.end()-1)
- 使用迭代器访问
//定义迭代器
set<int>::iterator it;
it = s.begin();
it++;
cout<<*it;//访问第二个元素
常用函数
- 插入和删除元素
s.insert(5); // 插入
s.erase(5); // 删除
- 访问元素
int value = *(s.begin());//地址通过 * 可以去访问该地址的值
for (auto it = s.begin(); it != s.end(); ++it) {
cout << *it << " ";
}
- 查找元素
if (s.find(5) != s.end()) {//如果未找到,find 返回 s.end()
cout << "元素 5 存在";
}
其他一些函数如下
其他set
集合 | 特点 |
---|---|
unordered_set | 非有序,存储数据的时候是杂乱无序的 |
multiset | 元素可以重复,且元素有序 |
unordered_multiset | 元素无序,且可重复 |
map (映射)
map是一个映射,就是一个元素对应另一个元素,映射类似于函数的对应关系,每个x对应一个y,而map是每个键对应一个值。
特点
- 每个键(key)对应一个值(value)。
- 键值对按照键自动排序。
- key不可以重复,value可以重复
定义
#include <map>
//map<关键字key数据类型, 值value的数据类型> 变量名
map<int, string> mp;
map会按照键的顺序从小到大自动排序,所以key的数据类型必须可以比较大小
常用操作
添加元素
方式一:使用类似数组的方式添加元素
mp[1] = "one" // 目前 mp{1->"one"}
mp[2] = "two" // 目前 mp{1->"one",2->"two"}
方式二:使用map内置的函数插入元素(需要构造键值对)
mp.insert({3, "three"});
mp.insert(pair<int, string>(4, "four"));
mp.insert(make_pair(5, "five"));
注意:使用insert插入map元素时,需要构造键值对
访问元素
cout << mp[1]; // 通过键访问
遍历
- 使用迭代器
// map的遍历:使用迭代器
map<string, int>::iterator it;
for (it = mp.begin(); it != mp.end(); it++) {
cout << it->first << " " << it->second << endl;
}
使用迭代器 it 有两种访问元素的方式:
// it是结构体指针访问所以要用 -> 访问
cout << it->first << " " << it->second << "\n";
//*it是结构体变量 访问要用 . 访问
cout << (*it).first << " " << (*it).second;
- 智能指针
for (auto i : mp) {
cout << i.first << " " << i.second << endl; // 键,值
}
常用函数
特别说明:
查找元素在容器中的具体地址,使用 mp.find()
查找元素是否存在,可以使用 mp.count()
auto it = mp.find(1);
if (it != mp.end()) {
cout << it->first << ": " << it->second;
}
unordered_map
与map的区别:
- map内部使用红黑树实现,具有自动排序(按键从小到大)功能
- unordered_map内部元素杂乱无序
结语
关于竞赛和笔试的一点小tips
一、如果在考场上忘记某个函数的头文件
- 系统自动提示:在IDE中,输入容器名称(如
vec
),点击系统提示,会自动补全所需的头文件。 - 使用通用头文件:如果不确定某个容器的头文件,直接包含所有常用头文件
#include <bits/stdc++.h>
二、如果忘记某个函数方法
- 函数提示:在线系统中,输入变量名加点号(如
vec.
),可以看到与该变量相关的函数列表,随机尝试一个即可。 - 源码查找:在本地 IDE 中,按住
Ctrl
键,点击容器类型(如vector
),可以跳转到源码,查看支持的函数。
但并不是所有函数都能用上面的方法寻找到,还是需要自行记住一部分,例如数学函数:pow()
, sqrt()
;字符处理:tolower()
;STL 算法: sort()
, transform()
等等.