文章目录
缘起
在用C++刷LeetCode的时候,经常会遇到一些关于STL的操作,但老是会忘记。故借此把常用的STL的操作记录下来,以备查询~
长期更新ing…
String 字符串
string
的某一位就是char
字符,string
就是char
类型的模板类。
1. string.find()
串查找函数
string str1, str2;
str1.find(str2, 0); // 从str1中查找str2,返回起始位置索引
str1.find(str2, 5); // 从str1中的第5个位置后开始查找str2
时间复杂度: 这个我查阅了很多资料,都说不是用的KMP算法,因为涉及到next数组的操作。而是权衡了效率和复杂度,最后用的简答匹配方法,最坏复杂度为O(M*N)。所以在一些要求复杂度的场合,还是老老实实自己写KMP吧。
2. string.substr()
求子串函数
string str1;
str1.substr(0,1); // 返回str1[0:1] 0位置后的一个字符
str1.substr(2,5); // 返回str1[2:2+5] 2位置后的五个字符
时间复杂度 这个不用考虑时间复杂度,就是一个拷贝过程。主要是参数问题,我老是以为两个都是索引值,其实只有第一个为索引,第二个参数为长度。
3. string.c_str()
string类型转为char*类型,这个常用于string转int中
string s = "12abc";
int n=atoi(s.c_str()); // n=12
P.S. 如果有其他的类型要转为string类型的话,直接用.to_string()
即可
4. string.erase()
删除string中某段字符串
string str = "012345";
str = str.erase(1,2); // 从位置"1"开始,删除2个字符
>> "0345"
5. pop_back()
弹出字符串中的最后一个字符,这个操作也适用于vector
string s="abcdefg";
s.pop_back();
>> "abcdef"
6. back()
返回最后一个字符
string s="abcdefg";
s.back();
>> "g"
7. append()
末尾添加字符/字符串
string s1 = "a", s2 = "xyz";
s1.append("bcd"); // bcd
s1.append(5, 'e'); // bcdeeeee
s1.append(s2); // bcdeeeeexyz
8. replace()
替换字符串,第一个参数表示插入给定字符串的起始字符。下一个参数指定应该被新字符串替换的子字符串的长度。最后,新字符串作为第三个参数传递。
string s = "abcde"
s.replace(0, 2,"fg"); // "fgcde" 将s[0:2]这两个字符串置换成"fg"
更详细:https://www.delftstack.com/zh/howto/cpp/string-replace-cpp/
Vector 动态数组
1. vector.resize()
重新设置vector大小
vector<int> a; // 现在a的size为0 增加数据只能用a.push_back()
a.resize(8); // 将a的size变为8
2. vector.erase()
删除vector中的元素
vector<int> a;
a.erase(a.begin()+2, a.end()); // 删除a[2]及后面所有的元素
a.erase(a.begin()+2) // 删除a[2]
a.erase(a.begin()+2, a.begin()+5) // 删除a[2]——a[4]
3. 二维vector初始化
vector<vector<int>> a;
// 指定大小和初始值
vector<vector<int>> b(5,vector<int> (5,1)); //得到5x5初始值为1的向量矩阵
4. assign 拷贝
// 1. 初始化时拷贝
vector<int> data(raw); // raw为被拷贝数据,类型同样为 vector<int>
// 2. assgin
data.assign(raw.begin(), raw.end());
5. insert 插入
插入元素
// 1. 插入单个元素
vector<int> a;
a.insert(a.end(), 100); // [100]
// 2. 插入多个相同元素
a.insert(a.end(), 3, 100); // [100, 100, 100]
// 3. 插入某个连续空间范围
vector<int> a;
int arr[] = {1, 2, 3};
a.insert(a.end(), arr, arr+3); // [1, 2, 3]
Queue 队列
1. 普通队列 queue
queue<int> q;
q.push(4); // 入队列
q.push(6);
cout<<q.front()<<endl; // 队列首部元素
cout<<q.back()<<endl; // 队列尾部元素
q.pop(); // 出队列
2. 双端队列 deque
采用动态数组来管理元素,提供随机存取,类似于vector。但deque的动态数组头尾都开放,因此能在头尾进行快速的插入和删除。
#include <deque> // 头文件
// 基本操作
deque<int> q;
q.push_back(4); // 插入队尾
q.push_front(6); // 插入队首
cout<<q.front()<<endl; // 队列首部元素
cout<<q.back()<<endl; // 队列尾部元素
q.pop_back(); // 删除队尾元素
q.pop_front(); // 删除队首元素
3. 优先队列 priority_queue
最优队列:其本质是一个堆,所以插入、删除操作时间复杂度为O(nlogn)。
其与队列基本操作相同:
top
访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)时间复杂度:log(n)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素时间复杂度:log(n)
swap 交换内容
这里介绍一下如何构造降序队列 / 升序队列
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
自定义构造比较方式
假设我们需要针对vector<pair<int, int> >
类型的数据构造降序队列,首先需要构建类,设置
// 自定义比较
class cmp{
public:
// 构造小顶堆:其比较函数与sort设置相反
bool operator()(pair<int, int>& m, pair<int, int>& n){
return m.second > n.second;
}
};
// 构造小顶堆
priority_queue<pair<int, int>, vector<pair<int, int> >, cmp> q;
需要注意的是,pair本身就已经重载了
<
和>
运算符,在比较的时候,先比较第一维,当第一维相等时再比较第二维,所以如果满足这个要求在构建优先队列时就不需要再自己定义比较函数了。
Stack 栈
1. stack基本操作
stack<int> s;
s.push(1); // 入栈
s.push(2);
s.top(); // 获取栈顶元素
s.pop(); // 出栈
unordered_set 无序集合
unordered_set是set的哈希实现,这是C++ 11.0标准新增的容器类型。相比于Set,它的插入和查询O(1)要快得多,因为unoredered_set是不需要进行排序的,而且是真正的Hash实现,而set是红黑树的实现。
头文件#include<unordered_set>
unordered_set选取原则
如果set不涉及到
排序、遍历
等操作,即可使用unordered_set,比set的效率要高很多
1. unordered_set初始化
unordered_set<string> a{"01", "02", "03"};
string str[]={"01", "02", "03"};
unordered_set<string> b(str, str+3);
2. unordered_set.count()
判断元素是否存在unordered_set中,count()函数本来是用来查找出现次数的,但是在这里只会返回0 / 1,所以用来判断元素是否存在。
a.count("01");
3. unordered_set.find()
和2.的作用是一样的,但是效率要高很多,如果没找到,返回.end()。
unordered_set<string> a,b;
a.find("01")!=a.end(); // 返回0 / 1
时间复杂度 :O(1)
4. unordered_set.insert()
插入元素
unordered_set<string> a;
a.insert("05");
时间复杂度: O(1)
5. unordered_set.erase()
删除元素
unordered_set<int> st={1,2,3};
st.erase(1);
>> {2,3}
unordered_map 无序哈希
在上面的unordered_set
中,我已经说过这个容器的一些特性,他是Map的Hash实现,对于遍历无要求的情况,这个效率要比map
快很多。
1. unordered_map初始化及插入
unordered_map<int, string> m = {{1, "Huang"},{2, "Zi"}};
m[3]="X";
m.insert(make_pair(4,"Y"));
2. unordered_map.find()
m.find(3)!=m.end(); // 1 or 0 元素是否在map中
时间复杂度: O(1)
3. unordered_map遍历及删除
for(it=m.begin(); it!=m.end(); it++){
cout<<"key: "<<it->first <<" value: "<<*it->second<<endl;
delete it->second;
m.erase(it++);
}
这种删除方式是STL源码一书中推荐的方式,分析m.erase(it++)
,map中在删除iter的时候,先将iter做缓存,然后执行iter++使之指向下一个结点,再进入erase函数体中执行删除操作,删除时使用的iter就是缓存下来的iter(也就是当前iter(做了加操作之后的iter)所指向结点的上一个结点)
第二种遍历方式:
for(auto i:m){
cout<<"key: "<<i.first <<" value: "<<*i.second<<endl;
}
pair 对组
将两个数据组合为一组数据,这个通过是用来作为辅助作用的。比如我先要让一个vector
一次存取两个数据,就可以用到pair。
举例
vector<pair<int, int>> a; // 定义
a.push_back(make_pair(1,2)); // 存储
atuo s=a[0]; // 读取
s.first; // 第一个值
s.second; // 第二个值
*min_element与 *max_element 求极值
返回连续空间中的最大/最小值
int a[6] = {5, 3, 2, 6, 1, 4};
*max_element(a, a+6) // 输出为 6
*min_element(a, a+6) // 输出为1
accumulate 求和
accumulate函数将它的一个内部变量设置为指定的初始值,然后在此初值上累加输入范围内所有元素的值
accumulate带有三个形参:头两个形参指定要累加的元素范围,第三个形参则是累加的初值
accumulate算法返回累加的结果,其返回类型就是其第三个实参的类型
// 求和
vector<int> num;
accumulate(num.begin(), num.end(), 0)
// string
vector<string> s;
accumulate(s.begin(), s.end(), string(" "))
bitset 二进制
bitset存储二进制数位,就和bool类型的数组差不多,但进行了空间优化:一个元素一般只占1 bit,bitset中的每个元素都能单独被访问,整数类型和布尔数组都能转化成bitset。这在我们进行位运算时能提供高效的辅助:
bitset<4> a; // 0000
bitset<4> b(10); // 1010
bitset<4> c(string("1011")); // 1011
b.count() // 返回1的个数