STL库里的常用容器
前言:之前的文章经常使用一些STL库提供的容器以及其包含的算法来简化代码编写,但都是让大家自己去学习下,不知道大家有没有去学下,本篇简单总结介绍下几种常用的容器及其使用。
string
字符串类与C语言char数组的不同:string没有字符串结尾标识'\0',不能用printf()输出,但C++提供了printf访问字符串的接口——函数data()或者c_str()
string s="hello world";
printf("%s\n%s",s.data(),s.data());
定义和初始化
string s; //创建一个空字符串
string s1="Hello world!"; //直接初始化
string s2("Hello world!"); //完全拷贝
string s2(s1);
string s2=s1;
string s2(s1,0,5); //部分拷贝,从下标0的位置开始取5个字符拷贝给s2
string s2("Hello world!",0,5)
string s2(s1,1); //从下标1的位置拷贝到末尾
string s3(4,'a'); //用4个字符'a'初始化字符串
string s2=s1.substr(0,5); //s.substr(起始位置下标,长度) 截取子串,若长度缺省则截取到最后
拼接
string s1="abc",s2="def";
string s3=s1+s2; //直接用'+'拼接
s1.append(s2); //使用连接函数append()
s1.append(s2,0,3); //可拼接部分(s2从下标1开始的3个字符)
s1.append(4,'d'); //在末尾添加4个'd'
s1.insert(2,2,'a'); //在下标为2的位置插入两个'a'
删除
string s1="hello world!";
s1.erase(5,7); //删除从第5个开始的7个字符
s1.erase(1); //删除从第1个开始往后所有的字符
遍历
string s="abcdef";
for(int i=0;i<s.size();i++){
cout<<s[i]; //通过下标访问
}
for(auto i:s){ //基于范围的for循环
cout<<i;
}
//也可使用迭代器但没必要
常用函数
string s;
cin>>s; //遇到空格会结束
getline(cin,s); //获取一行字符串,但会读取前面的换行符(若前面有cin,getline会处理自身结尾的换行符),可用cin.ignore()处理,丢弃前面的换行符
string s1="abc",s2="aaa";
cout<<(s1>s2); //字符串比较直接用>,<,==,!=
string s="abc";
cout<<s.size(); //返回字符串长度
s.clear(); //清空字符串
cout<<s.empty(); //判断字符串是否为空,是返回1,否返回0
string s1="hello world!";
s1.replace(6,5,"jane"); //将从第6位开始的5个元素替换为jane
s1.replace(6,5,"iamjane",3,4); //替换为后面字符串从第3位开始的4位个元素
s1.replace(6,5,4,'x'); //替换为4个'x'
string s1="hello world!"; //包含在<algorithm>头文件
if(s1.find("llo")!=-1){ //从左向右找第一次出现"llo"的位置,找到则返回第一个元素的下标,若没找到则返回-1
cout<<s1.find("llo");
}
s1.find('o',5); //从下标为5的位置开始寻找'o'
if(s1.rfind('o')!=-1){ //从右往左找第一次出现'o'的位置
cout<<s1.rfind('o');
}
if(s1.find_first_of("world")!=-1){ //从左往右找第一个出现的子串(字符串的任意子串)
cout<<s1.find_first_of("world"); //第一个子串是'l'
}
//find_first_not_of(),找第一个不是子串的位置
//find_last_of(),从右往左找第一个
string s="fedbca"; //s.end()是最后一个元素的后一个位置
sort(s.begin(),s.end()); //对s中元素从小到大排序(左闭右开)
string s="ABC";
reverse(s.begin(),s.end()); //翻转字符串,包含在<algorithm>头文件
字符串数组
string s[4]={"ling","yi","er","san"};
char a[10][15]={"ling","yi","er","san"}; //二维字符数组
vector
动态数组,是存储任意相同类型的、大小可变的数组,能自动管理内存(不像C语言中的数组在定义时要给定数组大小)。在使用其中的函数时要引入头文件
#include<iostream>
#include<vector> //引入<vector>头文件
using namespace std; //vector容器是属于std命名空间的
定义与初始化
vector<int>v1; //定义一个整型的空数组
vector<int>v2{1,2,3,4,5}; //列表初始化一个包含5个整形元素的数组
vector<int>v3(5); //定义一个大小为5的数组,元素默认为0
vector<int>v4(5,1); //定义一个大小为5的数组,元素都为1
vector<int>v5=v2; //可以直接用=拷贝
元素访问
cout<<v2[2]; //与普通数组一样,可通过下标访问(从0开始)
for(vector<int>::iterator it=v2.begin();it!=v2.end();it++){ //通过迭代器访问,定义为vector<type>::iterator it;
cout<<*it<<" "; //迭代器可以近似理解为指针,通过*it访问其元素
}
for(auto it=v2.begin();it!=v2.end();it++){ //也可用auto自动类型推导
cout<<*it<<" ";
}
for(auto rit=v2.rbegin();rit!=v2.rend();rit++){ //反向迭代,v.rbegin()+1是倒数第二项
cout<<*rit<<" ";
}
for(auto i:V2){ //还可用基于范围的for循环遍历v2中的每个元素
cout<<i<<" ";
}
cout<<v2.back(); //返回最后一项的值
添加元素
v2.push_back(6); //在数组末尾添加元素6
v2.insert(v.begin(),0); //在首元素前面插入一个0
v2.insert(v.begin()+2,6); //在下标为2的位置前面插入一个6
v2.insert(v.begin(),3,6); //在首元素前面插入3个6
v.insert(v.begin(),v2.begin(),v2.end()); //在v容器前面插入v2容器(容器拼接)
删除元素
v2.pop_back(); //删除数组的最后一个元素
v2.erase(v2.begin()+1); //删除下标为1的元素
v2.erase(v2.begin()+1,v2.begin()+5); //删除下标从1到4的一段元素(左闭右开)
v2.erase(v2.begin(),v2.end()); //删除所有元素
v2.clear(); //清除所有元素
常用函数
cout<<v2.size(); //返回数组中元素的个数
cout<<v.empty(); //判断数组是否为空,是返回true,否返回false
cout<<(v==v2); //可直接用==判断其中元素是否相等
sort(v.begin(),v.end()); //对v所有元素从小到大排序,包含在<algorithm>头文件中
sort(v.begin(),v.end(),cmp); //和普通sort一样,可自定义比较函数
reverse(v.begin(),v.end()); //将数组中的元素前后翻转,包含在<algorithm>头文件
auto it=find(v2.begin,v2.end(),2); //返回元素2在v2中第一次出现的位置,包含在<algorithm>头文件
if(it!=v2.end()){ //it是迭代器类型
cout<<"找到了!"<<endl;
cout<<it-v2.begin(); //返回第一次出现的下标
}
二维数组
vector<vector<int>>v; //创建一个空的二维数组,每个元素都是一个vector<int>类型的容器,每行的长度和列的长度都可动态改变(行与行在内存中不一定是连续存储的)
vector<int>v1[10]; //一个包含10个vector<int>元素的数组,也是二维数组,但第一维大小固定为10(在内存中连续存储)
vector<vector<int>>v2(n+1,vector<int>(m+1,0)); //大小为n+1的vector容器,每个元素是大小为m+1的vector容器,初始值为0
vector<vector<int>>v{{1,2},{3,4}};
v[0].push_back(3); //在某一行添加元素
v.push_back({5,6}); //添加新行 ||v.push_back(vector<int>{5,6})
vector<vector<int>>v{{1,2},{3,4}};
v[0].erase(v[0].begin(),v[0].end()); //删除v[0]中的所有元素,v[0]变成空数组,v的大小为1
//v变为 {{},{3,4}}
//v.erase(v[0].begin(), v[0].end())是错误的,erase方法需要的是外层容器的迭代器范围,而 v[0].begin()和v[0].end() 是子向量的迭代器
vector<vector<int>>v{{1,2},{3,4},{5,6}};
v.erase(v.begin()+1); //v[1]空间被删除,v的大小变为2,后续元素的索引向前移动
//v变为 {{1, 2}, {5, 6}}
v[0].clear(); //删除v[0]中的所有元素,v[0]变为空数组
pair
pair是一个模板类,它用于将两个不同类型的数据组合成一个单一的对象,通常用于表示键值对或其他需要组合两种数据的场景
初始化
pair<string,int>p1; //创建一个默认初始化的pair,pair.first="",pair.second=0
pair<string,int>p2{"Mike",1}; //直接初始化
pair<string,int>p3=make_pair("Mike",1); //通过make_pair函数
成员访问
cout<<p2.first<<" "<<p2.second; //使用"."运算符
pair支持比较操作,先比较first成员,若first
成员相等,则比较second
成员
pair<string,int>p1{"1",1};
pair<string,int>p2{"2",0};
if(p2>p1){
cout<<"p2>p1";
}
map
关联容器,以键值对的形式存储数据(每个键值对都是一个pair对象),键是唯一的,主要用于"一对一"映射的情况。map的内部逻辑是红黑树(一种自平衡二叉搜索树),会自动将数据按键从小到大有序存储,由于其基于红黑树的实现,查找、插入和删除操作的时间复杂度均为 O(logn)。
在使用时要包含头文件<map>
#include<map>
创建
map<string,int>mp1; //创建一个空的map
map<string,int>mp2{{"Jane",1},{"Mike",2}}; //初始化列表创建
map<string,int>mp3{mp2}; //拷贝另一个map创建
插入元素
mp.insert({"Mary",1}); //使用insert方法
mp["John"]=2; //类似数组的方式增加元素,若键存在,修改其对应值;若不存在,添加此键值对
查找元素
auto it=mp.find("Mary"); //使用迭代器(此处小编直接用auto了,若不嫌麻烦可与上文一样创建迭代器)
if(it!=mp.end()){
cout<<it->second;
}else{
cout<<"Not found";
}
cout<<mp["Mary"]; //使用下标运算符,若键不存在,会用默认值创建
删除元素
mp.erase("Mary"); //使用erase方法,删除键为"Mary"的元素
auto it=mp.find("Mary");
if(it!=mp.end()){
mp.erase(it); //删除迭代器指向元素
}
遍历元素
for(auto it=mp.begin();it!=mp.end();it++){ //使用迭代器
cout<<it->first<<" "<<it->second<<endl; //迭代器要用键的别名first,值的别名second
}
for(auto item:mp){ //基于范围的for循环
cout<<item.first<<" "<<item.second<<endl; //每个元素都是一个pair对象,用"."
}
By the way(顺便说一句,主播最近也在学英语,是这个意思吧),empty(),size(),clear()这些方法大部分都是通用的
set
关联容器,用于存储唯一元素(其中的元素不重复),set内部也使用红黑树来实现,会将元素自动从小到达排序,提供了高效的插入、删除和查找操作。
使用时需包含头文件<set>
#include<set>
创建
set<int>st1;
set<int>st2{1,2,3,4,5};
set<int>st3{st2};
插入,查找,删除
st2.insert(6); //插入元素6
auto it=st2.find(3); //查找元素3
if(it!=st2.end()){
cout<<*it;
}else{
cout<<"Not find";
}
st2.erase(3); //删除元素3
遍历元素
for(auto it=st2.begin();it!=st2.end();it++){ //使用迭代器
cout<<*it<<" ";
}
for(auto item:st2){ //基于范围的for循环
cout<<item<<" ";
}
queue
一种容器适配器,用于实现先进先出(FIFO,First-In-First-Out)的数据结构,其中元素从队尾插入,从队头移除,队列的大小可以根据需要动态调整。
使用需包含头文件<queue>
#include<queue>
声明和初始化
queue<int>q1;
queue<int>q1{q2};
//queue是一个容器适配器,其设计初衷是提供一个简单的先进先出数据结构,因此它不支持初始化列表的构造函数
插入、移除
q1.push(2); //在队尾插入元素2
q1.pop(); //移除队首元素
访问队头和队尾
cout<<q.front(); //队头
cout<<q.back(); //队尾
一个通用的好用STL算法
lower_bound()在一个已排序好的序列中,找到第一个大于等于(不小于)给定值的元素,并返回指向该元素的迭代器;如果容器中所有元素都小于给定值,则返回尾迭代器.查找逻辑是二分查找。
使用需包含头文件<algorithm>
使用示例
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
int main(){
set<int>st{1,2,4,6,8,10};
auto it=st.lower_bound(5);
if(it!=st.end()){
cout<<*it<<endl;
} else {
cout<<"All elements are less than 5"<<endl;
}
return 0;
}
upper_bound()同理,用于在有序序列中查找第一个大 给定值的元素。
unordered_set
内部基于哈希表实现(哈希表是一种通过哈希函数将键映射到存储位置的数据结构),能够快速地插入、查找和删除元素(比set快,对时间复杂度要求高可用),元素存储顺序是无序的,且是唯一的。
使用示例
#include<iostream>
#include<unordered_set>
#include<algorithm>
using namespace std;
int main(){
unordered_set<int>st{2,5,6,4,8,9};
st.insert(11);
auto it=st.find(4);
if(it!=st.end()){
st.erase(4); //st.erase(it)
}
for(int x:st){
cout<<x<<" ";
}
return 0;
}