一、初识STL
STL六大组件:容器、算法、迭代器、仿函数、适配器、空间配置器
1、容器
常用的容器有数组、链表、树、栈、队列、集合、映射等
分为序列式容器和关联式容器
序列式容器:强调值的排序,每个元素均有固定位置
关联式容器:二叉树结构,各元素间没有严格的物理上的顺序关系
2、算法
使用STL中的算法需有头文件
#include<algorithm>
质变算法:运算过程中会更改区间内的元素的内容,如拷贝、替换、删除等
非质变算法:指运算过程中不更改区间的元素内容,如查找、遍历等
3、迭代器
提供一种方法使之能依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表达式,扮演了容器与算法间的胶合剂。使用非常类似指针。每个容器都有自己专属的迭代器。
迭代器 | 功能 |
---|---|
输入迭代器 | 对数据的只读访问 |
输出迭代器 | 对数据进行只写访问 |
前向迭代器 | 读写操作,并能向前推进迭代器 |
双向迭代器 | 读写操作,并能向前和向后 |
随机访问迭代器 | 读写操作,可以以跳跃方式访问任意数据 |
随机访问迭代器是目前功能最强的迭代器,常用的迭代器为双向迭代器和随机访问迭代器
二、string容器
string是c++风格的字符串,而string本质是一个类
string与char的区别:
char是一个指针,string是一个类,类内部封装了char管理这个字符串是一个char型容器
头文件:
#include<string>;
1、构造函数
(1)默认构造
string s1;
(2)使用字符串初始化
const char*str="hello";
string s2(str);
(3)使用一个string对象初始化另一个string对象
string s3(s2);
(4)使用n个字符初始化
string s4(int n,char c);
2、赋值
(1)等号赋值
(2)assign赋值
s1.assign("hello");
s2.assign("hello",int n);//取“hello”的前n个字符
s3.assign(int n,char c);//将n个字符c赋值给s3
3、拼接
(1)+=拼接
str+="追加内容";
(2)append拼接
str.append("追加内容");
str.append("内容",int n);//将"内容"的前n个字符拼接
str.append(str1,int pos,int n);//从字符串str1的某个位置起截取n个字符与str进行拼接
4、查找与替换
(1)查找
str.find(要查找的字符或字符串,查找的起始位置);
从左往右找,起始位置默认为0
str.rfind(要查找的字符或字符串,查找的结束位置);
从右往左找
注:无论是用find还是rfind,返回的位置下标都是从左往右数的
str.find("ab",2);//返回“ab”第一次在str[2]到str[n-1]之间出现的位置
str.rfind("ab",2);//返回“ab”第一次在str[0]到str[2]之间出现的位置
(2)替换
str.replace(int pos,int n,字符/字符串);
替换从str的pos位置起的n个字符
5、比较
按字符的ASCII码进行比较
str1.compare(str2);
str1=str2返回0
str1>str2返回1
str1<str2返回-1
常用于判断两个字符串是否相同
6、存取
访问str位置下标为i的元素
str[i];
str.at(i);
位置下标从0开始
7、插入与删除
(1)插入
str.insert(位置下标,字符串);
str.insert(位置下标,int n,char c);在指定位置插入n个字符c
(2)删除
str.erase(起始位置下标,删除字符数);
8、获取子串
str.substr(起始位置下标,截取字符个数n);
三、vector容器
vector容器是最常用的容器,与数组相似,也称为单端数组
其与数组的不同之处是数组是静态空间,而vector可以动态扩展
(动态扩展并不是在原空间之后接新空间,而是找更大的内存空间,将原数据拷贝进新空间,释放原空间)
头文件:
#include<vector>
1、构造函数
创建一个vector容器
(1)无参构造
vector<数据类型> 变量名;
vector<int> v;
(2)通过区间的方式构造
vector<数据类型> 变量名(迭代器1,迭代器2);
将[迭代器1,迭代器2)区间中的元素拷贝给本身
vector<int> v1;
for(int i=0;i<10;i++){
v1.push_back(i);}
vector<int> v2(v1.begin(),v1.end());//区间前闭后开
(3)n个元素构造
vector<数据类型> 变量名(n,元素);
vector<int> v2(10,1000);
(4)拷贝构造
vector<int> v1;
for(int i=0;i<10;i++){
v1.push_back(i);}
vector<int> v2(v1);
2、通过迭代器访问数据
vector的迭代器是支持随机访问的
vector<int> v;
vector<int>::iterator it1=v.begin();//指向第一个元素
vector<int>::iterator it2=v.end();//指向最后一个元素的下一个位置
vector<int>::iterator it3=v.rend();//指向第一个元素的前一位
vector<int>::iterator it4=v.rbegin();//指向最后一个元素
3、插入和删除数据
push_back():在尾部插入数据
pop_back():删除尾部数据
insert(迭代器,元素):在迭代器指向的位置插入元素
insert(迭代器,n,元素):在迭代器指向的位置插入n个元素
erase(迭代器):删除迭代器指向的元素
erase(迭代器1,迭代器2):删除两个迭代器之间的元素
clear():清除所有元素
vector<int> v;
v.push_back(10);
v.pop_back();
v.insert(v.begin(),10);
v.insert(v.begin(),10,100);
v.erase(v.begin());
v.erase(v.begin(),v.end());//等价于v.clear()
v.clear();
4、赋值
(1)等号赋值
vector<int> v1;
vector<int> v2;
v2=v1;
(2)assign区间取值
v.assign(迭代器1,迭代器2);
vector<int> v1;
vector<int> v2;
v2.assign(v1.begin(),v1.end());//区间前闭后开
(3)n个元素
v.assign(n,元素)
vector<int> v1;
v1.assign(10,100);
5、遍历
(1)while循环
vector<int> v;
vector<int>::iterator it1=v.begin();//指向第一个元素
vector<int>::iterator it2=v.end();//指向最后一个元素的下一个位置
while(it1!=it2){
cout<<*it1<<endl;
it++;}
(2)for循环
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
cout<<*it<<endl;}
(3)使用遍历算法
#include<algorithm>//使用算法需要有对应头文件
void myprint(int val){
cout<<val<<endl;}
for_each(v.begin(),v.end(),myprint);
6、容量和大小
v.empty():判断容器是否为空,为空则为真
v.capacity():容器的容量
v.size():容器中元素的个数,即大小
v.resize(int n,元素):重新指定大小
如果比原来长,默认用0填充新位置,如果输入了元素,则用该元素填充
如果比原来短,则删除超出部分
注:容量>元素个数
7、数据存取
v[i]//访问第i个元素
v.at(i)//访问第i个元素
v.front()//访问第一个元素
v.back()//访问最后一个元素
8、存放自定义数据类型
struct student{
string name;
int score;
};
int main(){
vector<student> v;
student a[3]={
{"张三",80},
{"李四",90},
{"王五",88}};
v.push_back(a[0]);
v.push_back(a[1]);
v.push_back(a[2]);
for(vector<student>::iterator it=v.begin();it!=v.end();it++){
cout<<"姓名:"<<it->name<<"分数:"<<it->score<<endl;
}
system("pause");
return 0;
}
9、容器嵌套容器
vector<vector<int>> v;//创建大容器
vector<int> v1;
vector<int> v2;
vector<int> v3;//创建小容器
v1.push_back(10);
v2.push_back(20);
v3.push_back(30);//向小容器中添加数据
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);//将小容器插入大容器
遍历:
for(vector<vector<int>>::iterator it=v.begin();it!=v.end();it++){
for(vector<int>::iterator vit=(*it).begin();it!=(*it).end();vit++){
cout<<*vit<<endl;}}
10、互换容器
v1.swap(v2);
应用:收缩内存
可以使容量等于元素个数
vector<int>(v).swap(v);
其中vector(v)是一个匿名对象
11、预留空间
可以减少vector在动态扩展容量时的扩展次数
v.reserve(元素个数);
四、deque容器
deque容器也称为双端数组,可以对头端进行插入删除操作
deque内部有中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
deque容器的迭代器支持随机访问
头文件:
#include<deque>
deque与vector的区别:
(1)vector对于头部的插入删除效率低,数据量越大,效率越低
(2)deque相对而言对头部的插入删除速度比vector快
(3)vector访问元素时速度会比deque快
1、构造函数
与vector类似
(1)无参构造
deque<数据类型> 变量名;
deque<int> d;
(2)通过区间的方式构造
deque<数据类型> 变量名(迭代器1,迭代器2);
将[迭代器1,迭代器2)区间中的元素拷贝给本身
deque<int> d1;
for(int i=0;i<10;i++){
d1.push_back(i);}
deque<int> d2(d1.begin(),d1.end());//区间前闭后开
(3)n个元素构造
deque<数据类型> 变量名(n,元素);
deque<int> d2(10,1000);
(4)拷贝构造
deque<int> d1;
for(int i=0;i<10;i++){
d1.push_back(i);}
deque<int> d2(d1);
2、通过迭代器访问数据
deque的迭代器是支持随机访问的
deque<int>::iterator it1=d.begin();//指向第一个元素
deque<int>::iterator it2=d.end();//指向最后一个元素的下一个位置
3、赋值
与vector类似
(1)等号赋值
deque<int> d1;
deque<int> d2;
d2=d1;
(2)assign区间取值
d.assign(迭代器1,迭代器2);
deque<int> d1;
deque<int> d2;
v2.assign(d1.begin(),d1.end());//区间前闭后开
(3)n个元素
d.assign(n,元素)
deque<int> d1;
d1.assign(10,100);
4、大小操作
与vector相似,但deque无容量限制,没有容量概念
d.empty():判断容器是否为空,为空则为真
d.size():容器中元素的个数,即大小
d.resize(int n,元素):重新指定大小
如果比原来长,默认用0填充新位置,如果输入了元素,则用该元素填充
如果比原来短,则删除超出部分
5、插入与删除
push_back():在尾部插入数据
push_front():在头部插入数据
pop_back():删除尾部数据
pop_front():删除头部数据
insert(迭代器,元素):在迭代器指向的位置插入元素
insert(迭代器,n,元素):在迭代器指向的位置插入n个元素
insert(pos,beg,end):在pos位置插入[beg,end)区间的数据
d1.insert(d1.begin(),d2.begin(),d2.end());
erase(迭代器):删除迭代器指向的元素
erase(迭代器1,迭代器2):删除两个迭代器之间的元素
clear():清除所有元素
6、数据存取
与vector相似
d[i]//访问第i个元素
d.at(i)//访问第i个元素
d.front()//访问第一个元素
d.back()//访问最后一个元素
7、排序
sort(迭代器1,迭代器2);
对迭代器1和迭代器2区间内元素进行排序,默认从小到大升序排列
对于支持随机访问的迭代器都可以使用sort算法直接进行排序
#include<algorithm>//使用算法要有头文件
sort(d1.begin(),d1.end());
五、stack容器
stack(栈)是一种先进后出的数据结构,只有一个出口
栈中只有顶端元素才可以被外界使用,因此栈不允许有遍历行为
头文件
#include<stack>
1、构造函数
stack<int> stk;//默认构造
stack<int> stk1(stk);//拷贝构造
2、赋值
等号赋值
3、数据存取
stk.push(6);//向栈顶添加元素
stk.pop();//从栈顶移除第一个元素
stk.top();//返回栈顶元素
4、大小操作
stk.empty();//判断栈是否为空
stk.size();//返回栈的大小
5、取出栈中的数据
stack<int> stk;
stk.push(1);
stk.push(2);
stk.push(3);
stk.push(4);
while(!stk.empty()){
cout<<stk.top()<<" ";
stk.pop();
}
运行结果为:
4 3 2 1
先进后出:最先进入栈的元素最后取出
注:以上代码将元素不断移出栈,而遍历不会改变区间内的元素的内容,因此上述操作不是遍历
六、queue容器
queue是一种先进先出的数据结构,有两个出口,队列容器允许从一端新增元素,从另一端移除元素,从队尾进从队头出
队列中只有队头和队尾可以被外界使用,因此队列中不允许有遍历行为
头文件:
#include<queue>
1、构造函数
queue<int> q;//默认构造
queue<int> q1(q);//拷贝构造
2、赋值
等号赋值
3、数据存取
q.push(6);//向队尾添加元素
q.pop();//从队头移除第一个元素
q.front();//返回队头元素
q.back();//返回队尾元素
4、大小操作
q.empty();//判断队列是否为空
q.size();//返回队列的大小
5、取出队列中的数据
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while(!stk.empty()){
cout<<q.front()<<" ";
stk.pop();
}
运行结果为:
1 2 3 4
七、list容器
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表由一系列结点组成,结点又由存储数据元素的数据域和存储下一个结点地址的地址指针域组成
由于链表的存储方式并不是连续的内存空间,因此链表中的迭代器只支持前移和后移,属于双向迭代器
优点:
(1)采用动态存储分配,不会造成内存浪费和溢出
(2)链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
缺点:
链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
另外,插入和删除操作都不会造成原有list迭代器失效,这在vector中是不成立的
头文件:
#include<list>
1、构造函数
与vector类似
(1)无参构造
list<数据类型> 变量名;
list<int> l;
(2)通过区间的方式构造
list<数据类型> 变量名(迭代器1,迭代器2);
将[迭代器1,迭代器2)区间中的元素拷贝给本身
list<int> l1;
for(int i=0;i<10;i++){
l1.push_back(i);}
list<int> l2(l1.begin(),l1.end());//区间前闭后开
(3)n个元素构造
list<数据类型> 变量名(n,元素);
list<int> l2(10,1000);
(4)拷贝构造
list<int> l1;
for(int i=0;i<10;i++){
l1.push_back(i);}
list<int> l2(l1);
2、赋值
(1)等号赋值
(2)assign区间取值
l.assign(迭代器1,迭代器2);
list<int> l1;
list<int> l2;
l2.assign(l1.begin(),l1.end());//区间前闭后开
(3)n个元素
l.assign(n,元素)
list<int> l1;
l1.assign(10,100);
3、交换
list<int> l1;
list<int> l2;
l1.swap(l2);
4、大小操作
l.empty():判断容器是否为空,为空则为真
l.size():容器中元素的个数,即大小
l.resize(int n,元素):重新指定大小
如果比原来长,默认用0填充新位置,如果输入了元素,则用该元素填充
如果比原来短,则删除超出部分
5、插入和删除
push_back():在尾部插入数据
push_front():在头部插入数据
pop_back():删除尾部数据
pop_front():删除头部数据
insert(迭代器,元素):在迭代器指向的位置插入元素
insert(迭代器,n,元素):在迭代器指向的位置插入n个元素
insert(pos,beg,end):在pos位置插入[beg,end)区间的数据
l1.insert(l1.begin(),l2.begin(),l2.end());
erase(迭代器):删除迭代器指向的元素
erase(迭代器1,迭代器2):删除两个迭代器之间的元素
clear():清除所有元素
remove(elem):删除容器中所有与elem匹配的元素
6、数据存取
list不可用[]和at访问元素
l.front()//访问第一个元素
l.back()//访问最后一个元素
由于迭代器不支持随机访问,只能前后移动,所以:
list<int>::iterator it=l.begin();
it++;//正确
it--;//正确
it=it+1;//错误
it=it+2;//错误
7、反转
l.reverse();
8、排序
所有迭代器不支持随机访问的容器都不可用标准算法,但容器内部会提供一些算法
所以list中sort的使用与vector不同,不用带算法的头文件
l.sort();//默认从小到大升序排列
降序:
bool mycompare(int v1,int v2){
return v1>v2;}
l.sort(mycompare);
9、自定义数据类型的排序
#include<iostream>
#include<list>
#include<string>
using namespace std;
struct student{
string name;
int score;
int age;
};
void myprint(list<student>&L){
for(list<student>::iterator it=L.begin();it!=L.end();it++){
cout<<it->name<<" "<<it->score<<" "<<it->age<<endl;
}
}
bool mycompare(student stu1,student stu2){
if(stu1.score==stu2.score){
return stu1.age<stu2.age;
}//如果分数相同,则按年龄从小到大排序
return stu1.score>stu2.score;//按分数从大到小排序
}
int main(){
list<student>L;
struct student stu1={"张三",90,18};
struct student stu2={"李四",70,18};
struct student stu3={"王五",80,19};
struct student stu4={"赵六",80,17};
L.push_back(stu1);
L.push_back(stu2);
L.push_back(stu3);
L.push_back(stu4);
myprint(L);
L.sort(mycompare);
myprint(L);
system("pause");
return 0;
}
八、set/multiset容器
所有元素都会在插入时自动被排序
set/multiset容器属于关联式容器,底层结构是用二叉树实现的
头文件:
#include<set>
set与multiset的区别:
(1)set不可插入重复元素,而multiset可以
(2)multiset不会检测数据,因此可以插入重复元素
(3)set插入数据时会返回结果,表示是否插入成功
set<int>s;
pair<set<int>::iterator,bool>ret=s.insert(10);
if(ret.second){
cout<<"插入成功"<<endl;}
else{
cout<<"插入失败"<<endl;}
【pair对组】
成对出现的数据可以用对组返回两个数据
对组创建方法有两种:
(1)pair<type,type>p(value1,value2);
pair<string,int>p("Tom",32);
(2)pair<type,type>p=make_pair(value1,value2);
pair<string,int>p=make_pair("Tom",32);
访问数据
p.first//访问第一个数据
p.second//访问第二个数据
1、构造函数
(1)默认构造
set<数据类型> 变量名;
set<int> s;
(2)拷贝构造
set<int> s1;
for(int i=0;i<10;i++){
s1.insert(i);}
set<int> s2(s1);
2、赋值
等号赋值
3、插入和删除
insert():插入
erase(迭代器):删除迭代器指向的元素
erase(迭代器1,迭代器2):删除两个迭代器之间的元素
clear():清除所有元素
erase(elem):删除容器中与elem匹配的元素
4、交换
s1.swap(s2);
5、大小操作
s.empty();//判断容器是否为空,为空则为真
s.size();//容器中元素的个数,即大小
6、查找与统计
s.find(key):查找key是否存在,若存在则返回该键的元素的迭代器,若不存在则返回set.end()
s.count(key):统计key元素的个数
7、排序
(1)内置类型指定排序规则
set默认从小到大排序,利用仿函数可以改变排序规则
以降序排列为例:
class mycompare{
public:
bool operator()(int v1,int v2){
return v1>v2;}
}
set<int,mycompare>s;
for(set<int,mycompare>::iterator it=s.begin();it!=s.end();it++){
cout<<*it<<" ";}
(2)自定义类型指定排序规则
自定义数据类型都需要指定排序规则
以按成绩降序排列为例:
#include<iostream>
#include<set>
#include<string>
using namespace std;
struct student{
string name;
int score;
int age;
};
class mycompare{
public:
bool operator()(student stu1,student stu2){
return stu1.score>stu2.score;
}
};
void myprint(set<student,mycompare>&s){
for(set<student,mycompare>::iterator it=s.begin();it!=s.end();it++){
cout<<it->name<<" "<<it->score<<" "<<it->age<<endl;
}
}
int main(){
set<student,mycompare>s;
struct student stu1={"张三",90,18};
struct student stu2={"李四",70,18};
struct student stu3={"王五",80,19};
struct student stu4={"赵六",60,18};
s.insert(stu1);
s.insert(stu2);
s.insert(stu3);
s.insert(stu4);
myprint(s);
system("pause");
return 0;
}
九、map/multimap容器
map中所有元素都是pair
pair中第一个元素为key(键值)起索引作用,第二个元素为value(实值)
map/multimapt容器属于关联式容器,底层结构是用二叉树实现的
头文件:
#include<map>
所有元素都会根据键值自动排序
map不允许有重复的key值,而multimap允许
优点:可根据key值快速找到value值
1、构造函数
(1)默认构造
map<type,type>变量名
map<int,int>m;
(2)拷贝构造
map<int,int>m1(m2);
2、赋值
等号赋值
3、插入和删除
(1)插入
m.insert(pair<int,int>(1,10));
m.insert(make_pair(2,20));
m.insert(map<int,int>::value_type(3,30));
m[4]=40;//不建议用[]进行插入操作,可以利用m[key]查找value值
(2)删除
erase(迭代器):删除迭代器指向的元素
erase(迭代器1,迭代器2):删除两个迭代器之间的元素
clear():清除所有元素
erase(key):按key值删除元素
4、大小操作
m.empty();//判断容器是否为空,为空则为真
m.size();//容器中元素的个数,即大小
5、交换
m1.swap(m2);
6、查找与统计
m.find(key):查找key是否存在,若存在则返回该键的元素的迭代器,若不存在则返回set.end()
m.count(key):统计key元素的个数
7、排序
(1)内置类型指定排序规则
set默认从小到大排序,利用仿函数可以改变排序规则
以降序排列为例:
class mycompare{
public:
bool operator()(int v1,int v2){
return v1>v2;}
}
<int,mycompare>s;
for(map<int,int,mycompare>::iterator it=m.begin();it!=m.end();it++){
cout<<*it.first<<" "<<*it.second<<endl;}
(2)自定义类型指定排序规则
自定义数据类型都需要指定排序规则
与set类似