关于STL(标准模板库)
Standard Template Library,缩写:STL
STL是一个C++软件库,里面包含算法(algorithms)、容器(containers)、函数(functions)、迭代器(iterators)
C语言版本
#include<stdio.h>
int main(){
int a,b;
scanf("%d%d",&a,&b);
printf("%d",a+b);
return 0;
}
C++版本
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
cin>>a>>b;
cout<<a+b;
return 0;
}
C++也可以兼容C语言
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
scanf("%d%d",&a,&b);
printf("%d",a+b);
return 0;
}
C++中的引用
#include<bits/stdc++.h>
using namespace std;
void swap2(int &a,int &b){
int c=a;
a=b;
b=c;
}
int main(){
int a,b;
cin>>a>>b;
swap2(a,b);//交换两个数的值
cout<<a<<""<<b;
return 0;
}
引用是C++新增的内容,就同C语言的指针一样重要,但它的指针更加方和易用,有时候甚至是不可或缺的。同指针一样,引用能够减少数据的拷贝,提高数据的传递效率。
字符串(String)
String是STL中的一个重要的部分,主要用于字符串处理。可以使用输入输出流方式直接进行String读入输出,类似于C语言中的字符数组,由C++的算法库对String类也有着很好的支持,大多时候字符串处理的问题使用String要比字符数组更加方便。
博客推荐:https://www.cnblogs.com/nanyangzp/p/3607534.html
字符串(String)
创建String类型变量
- String s;直接创建一个空的(大小为0)的String类型变量s
- String s = *char;创建String时直接用字符串内容对其赋值,注意字符串要用双引号“”
- String s(int n,char c);创建一个String,由n个c组成,注意c是字符型要用单括号‘ ’
读入String
- cin>>s;读入s,遇到空格或回车停止,无论原先s是什么内容都会被新读入的数据替代
- getline(cin,s);读入s,空格也同样会读入,直到回车才会停止
输出String
- cout<<s;将s全部输出到一行(不带回车)
赋值、比较、连接运算符:
赋值运算符:=将后面的字符串赋值给前面的字符串O(n)
比较运算符:== != < <= > >=比较的是两个字符串的字典序大小O(n)
连接运算符:+ +=将一个运算符加到另一个运算符后面O(n)
-
s[index]返回字符串s中下标为index的字符,String中下标也是从0开始O(1)
-
s.substr(p,n)返回从s的下标p开始的n个字符组成的字符串,如果n省略就取到底O(n)
-
s.length()返回字符串的长度O(1)
-
s.empty()判断s是否为空,空返回1,不空返回0,O(1)
-
s.erase(p0,len)删除s中从p0开始的len个字符,如果len省略就删到底O(n)
-
s.erase(s.begin()+i)删除下标为i个字符O(n)
-
s1.insert(p0,s2,pos,len)后两个参数截取s2,可以省略O(n)
-
s.insert(p0,n,c)在p0处插入n个字符c O(n)
-
s1.replace(p0,len0,s2,pos,len)删除p0开始的len0个字符,然后在p0处插入串s2中从pos开始的len个字符,后两个参数可以省略O(n)
-
s1.find(s2,pos)从前往后,查找成功时返回第一次出现的下标,失败返回string::npos的值(-1)O(n*m)
-
s1.rfind(s2,pos)从pos开始从后向前查找字符串s2中字符串在当前串后边第一次出现的下标O(n*m)
动态数组(vector)
vector容器是STL中最常用的容器之一,它和array容器非常类似,都可以看做是对C++普通数组的“升级版”。不同之处在于,array实现的是静态数组(容量固定的数组),而vector实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector会动态调整所占用的内存空间,整个过程无需人工干预。
博客推荐:http://blog.csdn.net/w_linux/article/details/71600574
- vector<type> v创建动态数组v,后面可以加{}或()进行初始化
- type v[index]获取v中第index个元素O(1)
- v.push_back(type item)向v后面添加一个元素item O(1)
- v.pop_back()删除v最后一个元素O(1)
- v.size()获取v中元素个数,返回size_type类型O(1)
- v.resize(int n)把v的长度设定为n个元素O(n)
- v.empty()判断v是否为空,空返回1,不空返回0,O(1)
- v.clear()清空v中的元素O(size)
- v.insert(iterator it,type x)向迭代器it指向元素前增加一个元素x,O(n)
- v.erase(iterator it)删除向量中迭代器指向元素,O(n)
- v.front()返回首元素的引用O(1)
- v.back()返回尾元素的引用O(1)
- v.begin()返回首迭代器,指向第一个元素O(1)
- v.end()返回尾迭代器,指向向量最后一个元素的下一个位置O(1)
vector创建代码样例:
//vector的创建
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> v1;
//创建一个存int类型的动态数组,int可以改成其他类型
vector<double> v2{1,1,2,3,5,8};
//创建一个存double类型的动态数组,长度为6,1 1 2 3 5 8分别存在v[0]-v[5]
vector<long long> v3(20);
//创建一个存long long类型的动态数组,长度为20,v[0]-v[19]默认为0
vector<String> v4(20,"zzuacm");
//创建一个存String类型的动态数组,长度为20,存的都是"zzuacm"
vector<int> v5[3];
//相当于存int的二维数组,一共3行,每行的列可变
vector<vector<int>> v5{{1,2},{1},{1,2,3}};
//存int的二维数组,行和列都可变,初始状态
return 0;
}
int main(){
vector int v;
for(int i=1;i<=5;i++)
v.push_back(i);//向动态数组中插入1~5
cout<<v.size()endl;//输出数组的大小,有几个值
for(int i=0;i<5;i++)
cout<<v[i]<<" ";//输山v[0]~v[4],也就是1~5
cout<<endl;
v.clear();//将v清空,此时size为0
v.resize(10);//为v重新开辟大小为10的空间,初始为0
for(int i=0;i<v.size();i++)
cout<<v[i]<<" ";//遍历每一个元素
while(!v.empty())//当v还不空的话,去掉v的最后一个元素,等同于v.clear()
v.pop_back();
return 0;
}
int main(){
vector<int> v{0,1,2,3,4};
v.erase(v.begin()+3);//删除v[3],v变为{0,1,2,4}
v.insert(v.begin()+3,666);//在v[3]前加上666,v变成{0,1,2,666,4}
v.front()=10;//将v[0]改成10,等同于v[0]=10;
v.back()=20;//将v[4]改成20等同于v[v.size()-1]=20;
for(int i=0;i<v.size();i++)
cout<<v[i]<<" ";//使用下标访问的方法遍历v
cout<<endl;
for(auto i=v.begin();i!=v.end();i++)
cout<<i<<" ";//使用迭代器,从v.begin()到v.end()-1
cout<<endl;
for(auto i:v)//使用C++新特性循环遍历v,如果需要改变i的值,还需要在前面加上&
cout<<i<<" ";
cout<<endl;
return 0;
}
关于迭代器(iterator)
vector<int>::iterator it=v.begin();等价于auto it=v.begin();
在这里it类似于一个指针,指向v的第一个元素
it等价于&v[0]
*it等价于v[0]
it也可以进行加减操作,例如it+3指向第四个元素
it++后it指向的就是v的第二个元素(v[1])了
队列(queue)
queue只能在容器的末尾添加新元素,只能从头部移除元素。
博客推荐:https://www.cnblogs.com/hdk1993/p/5809180.html
创建方法:
queue<type> q;建立一个存放数据类型为type的队列q
使用方法:
- q.push(item):在q的最后添加一个type类型元素item O(1)
- q.pop():使q最前面的元素出队O(1)
- q.front():获取q最前面的元素O(1)
- q.size():获取q中元素个数O(1)
- q.empty():判断q是否为空,空返回1,不空返回0 O(1)
优先队列(priority_queue)
priority_queue中出队顺序与插入顺序无关,与数据优先级有关,本质是一个堆
博客推荐:https://blog.csdn.net/qq_41822647/article/details/88899915
创建方法:
priority_queue<Type, Container, Functional>
// Type:数据类型
// Container:存放数据的容器,默认用的是 vector
//Functional:元素之间的比较方法,当type可以比较时后两个可以省略
使用方法:
- pq.push(item):在pq中添加一个元素 O(logn)
- pq.top():获取pq最大的元素 O(1)
- pq.pop():使pq中最大的元素出队 O(logn)
- pq.size():获取pq中元素个数 O(1)
- pq.empty():判断pq是否为空 O(1)
样例:
#include<bits/stdc++.h>
using namespace std;
int main(){
priority_queue<int> pq;
//priority_queue<int,vector<int>,greater<int>> pq;
pq.push(1);
pq.push(3);
pq.push(2);
while(!pq.empty()){
cout<<pq.top()<<endl;
pq.pop();
}
return 0;
}
Output:
3
2
1
#include <bits/stdc++.h>
using namespace std;
struct Node{
int x,y;
};
bool operator<(Node a,Node b){
return a.x<b.x;
}
int main(){
priority_queue<Node> pq;
pq.push({1,3});
pq.push({3,2});
pq.push({2,1});
while(!pq.empty()){
cout<<pq.top().x<<' '<<pq.top().y<<endl;
pq.pop();
}
return 0;
}
Output:
3 2
2 1
1 3
priority_queue中存的元素如果是结构体这样无法进行比较的类型,必须要重载运算符<,相当于先使得优先队列中的元素可以进行比较再建立pq,否则直接建优先队列是会报错的
集合(set)
集合(set)是一种包含对象的容器,可以快速地(logn)查询元素是否在已知几集合中。
set中所有元素是有序地,且只能出现一次,因为set中元素是有序的,所以存储的元素必须已经定义过[<]运算符(因此如果想在set中存放struct的话必须手动重载[<]运算符,和优先队列一样)
与set类似的还有:
- multiset元素有序可以出现多次
- unordered_set元素无序只能出现一次
- unordered_multiset元素无序可以出现多次
博客推荐:https://www.cnblogs.com/zyxStar/p/4542835.html
集合(set)
建立方法:
*set<Type> s;
multiset<Type> s;
unorded_set<Type> s;
unorded_multiset<Type> s;
如果Type无法进行比较,还需要和优先队列一样定义<运算符
遍历方法:
for (auto i:s)cout<< i <<" ";
//和vector的类似
使用方法:
-
s.insert(item):在s中插入一个元素 O(logn)
-
s.size():获取s中元素个数 O(1)
-
s.empty():判断s是否为空 O(1)
-
s.clear():清空s O(n)
-
s.find(item):在s中查找一个元素并返回其迭代器,找不到的话返回s.end() O(logn)
-
s.begin():返回s中最小元素的迭代器,注意set中的迭代器和vector中的迭代器不同,无法直接加上某个数,因此要经常用到–和++ O(1)
-
s.end():返回s中最大元素的迭代器的后一个迭代器 O(1)
-
s.count(item):返回s中item的数量。在set中因为所有元素只能在s中出现一次,所以返回值只能是0 或1,在multiset中会返回存的个数 O(logn)
-
s.erase(position):删除s中迭代器position对应位置的元素 O(logn)
-
s.erase(item):删除s中对应元素 O(logn)
-
s.erase(pos1,pos2):删除[pos1,pos2]这个区间的位置的元素 O(logn+pos2-pos1)
-
s.lower_bound(item):返回s中第一个大于等于item的元素的迭代器,找不到就返回s.end() O(logn)
-
s.upper_bound(item):返回 s中第一个大于item的元素的迭代器,找不到就返回s.end() O(logn)
映射(map)
map是照特定顺序存储由key和value的组合形成的元素的容器,map中元素按照key进行排序,每个key都是唯一的,并对应着一个value,value可以重复。
map的底层实现原理与set一样都是红黑树。
与map类似的还有unordered_map,区别在于key不是按照顺序排序
博客推荐:https://www.cnblogs.com/panweiwei/p/6657583.html
建立方法:
map<key,value> mp;
unordered_map<key,value> mp;
遍历方法:
for(auto i:mp)
cout<<i.first<<’ '<<i.second<<endl;
map的常用函数:
-
mp.size():获取mp中元素个数 O(1)
-
mp.empty():判断mp是否为空 O(1)
-
mp.clear():清空mp O(1)
-
mp.begin():返回mp中最小key的迭代器,和set一样,只可以用到–和++操作 O(1)
-
mp.end():返回mp中最大key 的迭代器的后一个迭代器 O(1)
-
mp.find(key):在mp中查找一个key 并返回其iterator,找不到的话返回s.end() O(logn)
-
mp.count(key):在mp中找key的数量,因为map中key唯一,所以只会返回0或1 O(logn)
-
mp.erase(key):在 mp 中删除key 所在的项,key和value都会被删除 O(logn)
-
mp.lower_bound(item):返回mp中第一个key大于等于item的迭代器,找不到就返回mp.end()0(logn)
-
mp.upper_bound(item):返回 mp中第一个key大于item的迭代器,找不到就返回mp.end() O(logn)
-
mp[key]:返回 mp中 key 对应的 value。如果key 不存在,则返回 value 类型的默认构造器(defaultconstructor)所构造的值,并该键值对插入到mp中 O(logn)
-
mp[key] = xxx:如果mp中找不到对应的key则将键值对(key:xxx)插入到mp中,如果存在key则将这个key对应的值改变为xxx O(logn)
unordered的作用:
set和map都可以在前面加上unorder_使得内部的元素改成不按顺序存储的,其余的功能都不改变,虽然无法顺序存储,但底层原理是hash,可以使得所有的查询、修改、删除操作都变成O(1)复杂度
二进制有序集(bitset)
bitset是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间。
博客推荐:https://blog.csdn.net/qq_52023825/article/details/122409539
建立方法:
- bitset<n> b;//建立一个可以存n位二进制的有序集
- bitset<n> b(unsigned long u);//坐标从后往前计数,高位在前
- bitset<n> b(String s);//将由‘0’和‘1’组成的字符串赋值给支持所有位操作:| |= & &= << <<= >> >>=
- cout<< b;//bitset可以直接用cout输出b[index];//可以用下标操作符来读或写某个索引位置的二进制位
//比如bitset<10>b(5);等价于bitset<10>b(“1010”);
//其中cout<<b输出0000001010
//for(int i=0;i<10;i++)cout<<b[i];输出0101000000
使用方法:
-
b.count();//count函数用来求bitset中1的位数
-
b.size();//size函数用求求bitset的大小
-
b.any();//any函数检否bitset中是否有1
-
b.none();//none函数检查bitset中是否没有1
-
b.all();//all函数检查bitset中是全部为1
-
foo.flip();//flip函数不指定参数时,将bitset每一位全部取反
-
foo.set();//set函数不指定参数时,将bitset的每一位全部置为1
-
foo.reset();//reset函数不传参数时将bitset的每一位全部置为0
-
String s = foo.to_string();//将bitset转换成String类型
-
unsigned long a=foo.to_ulong();//将bitset转换成unsigned long类型
-
unsigned long long b=foo.to_ullong();//将bitset转换成unsigned long long类型
常用函数:
- max(val1,val2):返回更大的数
- min(val1,val2):返回更小的数
- swap(type,type):交换两者的值,可以是两个值也可以是两个STL的容器
排序函数(sort)
sort(first, last, compare)
- first:排序起始位置(指针或 iterator)
- 1ast:排序终止位置(指针或iterator)
- compare:比较方式,可以省略,省略时默认按升序排序,如果排序的元素没有定义比较运算(如结构体),必须有compare
sort排序的范围是[first,last],时间为 O(nlogn)
sort基本样例:
#include<bits/stdc++.h>
using namespace std;
bool cmp(int a,int b){
return a>b;
}
int main(){
int arr[]={3,2,5,1,4};
sort(arr,arr+5);
sort(arr,arr+5,greater<int>());
sort(arr,arr+5,cmp);
sort(arr,arr+5,[](int a,int b){returm a>b;});
return 0;
}
去重函数(unique)
unique(first, last):
- [first,last]范围内的值必须是一开始就提前排好序的
- 移除[first,last]内连续重复项
- 去重之后的返回最后一个元素的下一个地址(迭代器)
#include<bits/stdc++.h>
using namespace std;
int main(){
int arr[]{3,2,2,1,2},n;
sort(arr,arr+5);//需要先排序
n=unique(arr,arr+5)-arr;//n是去重后的元素个数
return 0;
}
二分函数(lower_bound/upper_bound)
lower_bound(first,last,value)
-
first:查找起始位置(指针或 iterator)
-
last:查找终止位置(指针或iterator)
-
value:查找的值
-
lower_bound 查找的范围是[first,last],返回的是序列中第一个大于等于value的元素的位置,时间为 O(logn)
-
[first,last]范围内的序列必须是提前排好序的,不然会错
-
如果序列内所有元素都比value小,则返回last
upper_bound(first, last, value)
- upper_bound与 lower_bound相似,唯一不同的地方在于upper_bound查找的是序列中第一个大于value的元素
二分函数样例:
int main(){
int arr[]={3,2,5,1,4};
sort(arr,arr+5);//需要先排序
cout<< *lower_bound(arr,arr+5,3);//输出数组中第一个大于等于3的值
return 0;
}
next_permutation
next_permutation(first,last)
用于求序列[first,last]元素全排列中一个排序的下一个排序
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[3] {0,1,2};
do{
for(int i=0;i<3;i++)
cout<<a[i]<<' ';
cout<<endl;
}
while(next_permutation(a,a+3));
return 0;
}