STL(标准模板库) 简介
STL 简介: STL(Standard Template Library) 标准模板库,是“容器”的集合。
STL 中常见的集合有:向量 (vector)、 栈 (stack)、 队 列 (queue)、 优先队列 (priority queue)、 链表 (list)、 集合 (set)、 映射 (map) 等容器。
C++标准模板库的核心包括以下三个组件:
组件 | 描述 |
---|---|
容器(Containers) | 容器是用来管理某一类对象的集合。C++提供了各种不同类型的 容器,比如deque、list、vector、map等。 |
算法(Algorithms) | 算法作用于容器。它们提供了执行各种操作的方式,包括对容器 内容执行初始化、排序、搜索和转换等操作。 |
迭代器(iterators) | 迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能 是容器的子集。 |
STL内的所有组件都由模板(template)构成,其元素可以是任意类型。
1 向量(vector)
1.1 什么是vector
向量 (vector) 是一个顺序容器 (Sequence Container),它能够存放各种类型的对象。
可以简单的认为, 向量是一个能够存放任意类型的动态数组(元素个数可变)。
1.2 vector的常见函数
函数名 | 函数说明 |
---|---|
push_back(元素) | 增加一个元素到向量后面 |
insert(位置,元素) | 插入元素到向量的指定位置 |
insert(位置,个数n,元素) | 插入n个相同的元素到指定位置 |
insert(位置,向量头指针 first,向量尾指针end) | 将另一个向量从first位置开始到end结束(不包括end)之间的内容 插入该向量的指定位置。 |
erase(位置) | 删除指定位置的元素 |
erase(开始位置,结束位置 ) | 删除向量中(first,last)中元素 |
pop_back() | 弹出(删除)向量的最后一个元素 |
clear() | 清除向量所有元素,size()变为0 |
运算符[i] | 取向量下标为i的元素 |
front() | 取向量第一个元素 |
back() | 取向量最后一个元素 |
begin() | 返回向量头指针(迭代器),指向第一个元素 |
end() | 返回向量尾指针,指向向量最后一个元素的下一个位置 |
rbegin() | 反向迭代器,指向最后一个元素 |
rend() | 反向迭代器,指向第一个元素之前的位置 |
size() | 返回向量中实际元素的个数。 |
resize(大小) | 重新设定向量的大小,也就是可以保存元素的个数。 |
max_size() | 得到vector最大可以是多大。 |
empty() | 判断向量是否为空,等价于size()为0。 |
swap() | 交换两个同类型向量的数据。 |
对应于数组,要注意:向量的大小是可变的,开始时向量为空,随着不断插入元素,向 量自动申请空间,容量变大。
注意学会使用: sort()、reverse()等函数对vector进行排序、逆序等操作。
1.3 例子:vector的存储和遍历
注意掌握 vector构造的4个常见方法:
(1)vector():创建一个空 vector
(2)vector(n):创建一个元素个数为 n 的 vector
(3) vector(n,t):创建一个元素个数为 n 且值为 t 的 vector
(4)vector(begin,end):复 制(begin,end)区间内另一个数组的元素到 vector中
#include<bits/stdc++.h>
using namespace std;
void print(vector<int> v) {
//遍历 vector 中的元素
for(int i=0; i<v.size(); i++) {
//vector: 可以使用下标访问,但不能越界
cout<<v[i]<<" ";
}
}
int main() {
//定义方法一:定义 vector 存储 int 类型的变量
//vector<int> v;
//size(): 获取 vector 存储元素的个数
//cout<<v.size()<<endl;
//注意:常见错误, vector<int>v 定义一个空 vector, 不可以用下标θ和1来访问元素
//v[0]=10;
//v[1]=20;
//v.push_back(10);
//v.push_back(20);
//v.push_back(30);
//cout<<v.size()<<endl;
//print(v);
//定义方法二:定义一个长度为5的 vector, 默认值为0
//vector<int> v(5);
//定义方法三:定义长度为5的 vector, 默认值为80
//vector<int> v(5,80);
//定义方法四:使用数组初始化 vector
int a[]={10,20,30,40,50};
//sizeof(): 计算变量占用的字节数
//sizeof(a)/sizeof(int): 计算数组的实际长度
cout<<sizeof(a)/sizeof(int)<<endl;
vector<int> v(a,a+sizeof(a)/sizeof(int));
v.push_back(10);
print(v);
return 0;
}
1.4 例子:vector的插入、删除、获取头尾元素
#include<bits/stdc++.h>
using namespace std;
void print(vector<int> v) {
//遍历 vector 中的元素
for(int i=0; i<v.size(); i++) {
//vector:可以使用下标访问,但不能越界
cout<<v[i]<<" ";
}cout<<endl;
}
int main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
cout<<"第一个元素:"<<v.front()<<" "<<*v.begin()<<endl;
cout<<"最后一个元素:"<<v.back()<<endl;
print(v);
//insert(位置,元素):位置必须提供位置指针
//向 vector 中下标为1的位置插入元素100
v.insert(v.begin()+1,100);
print(v);
//向 vector 中 下 标 为 1 的 位 置 插 入 5 个 1 0 0
v.insert(v.begin()+1,5,100);
print(v);
vector<int> v2;
v2.push_back(100);
v2.push_back(200);
v2.push_back(300);
//向 vector 下标为1的位置插入 v2 中下标为1到最后的所有元素
v.insert(v.begin()+1,v2.begin()+1,v2.end());
print(v);
//删除 vector 中下标为1的元素
v.erase(v.begin()+1);
print(v);
//从向量中下标为1的元素开始删除到最后
v.erase(v.begin()+1,v.end());
print(v);
return 0;
}
1.5 例子: resize()、swap()、sort()、reverse() 函数
#include<bits/stdc++.h>
using namespace std;
void print(vector<int> v) {
//遍历 vector 中的元素
for(int i=0; i<v.size(); i++) {
//vector: 可以使用下标访问,但不能越界
cout<<v[i]<<" ";
}
cout<<endl;
}
int main() {
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
vector<int> v2;
v2.push_back(100);
v2.push_back(300);
swap(v1,v2);
cout<<"v1:";
print(v1);
cout<<"v2:";
print(v2);
int a[]= {5,3,1,4,2};
vector<int> v(a,a+5);
sort(v.begin(),v.end());
print(v);
reverse(v.begin(),v.end());
print(v);
//重置 vector的大小为20
v.resize(2);
print(v);
return 0;
}
1.6 例子:二维 vector
#include<bits/stdc++.h>
using namespace std;
int main() {
//定义二维 vector
vector<vector<int>> v;
for(int i=0;i<5;i++) {
//需要先建立一个一维的vector,紧接着push_back
vector<int> temp;
for(int j=0;j<5;j++) {
temp.push_back(i+j);
}
v.push_back(temp);
}
//输出
for(int i=0;i<5;i++) {
for(int j=0;j<5;j++) {
cout<<v[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
2 栈(stack)
2.1 什么是栈
栈是一种后进先出 (First in last out, 简称 FILO 或者LIFO) 的元素序列,访问和删除都只能 对栈顶的元素(即最后一个被加入栈的元素)进行,并且元素也只能被添加到栈顶。栈内的 元素不能访问。如果一定要访问栈内的元素,只能将其上方的元素全部从栈中删除,使之变 成栈顶元素才可以。
stack 容器有广泛的应用。例如,编辑器中的 undo (撤销)机制就是用堆栈来记录连续的变化。 撤销操作可以取消最后一个操作,这也是发生在堆栈顶部的操作。
2.2 栈的常见函数
函数名 | 函数说明 |
---|---|
push(元素) | 入栈 |
pop() | 出栈 |
top() | 返回栈顶元素 |
size() | 返回元素个数 |
empty() | 判断栈是否为空 |
2.3 栈的存入和遍历
#include<bits/stdc++.h>
using namespace std;
int main() {
// stack<int> s;
// s.push(10);//入栈
// s.push(20);
// s.push(30);
// cout<<s.top()<<endl;
// s.pop();// 出栈
// cout<<s.top()<<endl;
/*
向栈中存入元素,直到遇到-1结束
*/
stack<int> s;
int x;
while(1){
cin>>x;
if(x==-1) break;
s.push(x);
}
//将元素逐个访问(将元素逐个弹出)
while(s.empty()==false) {
cout<<s.top()<<endl;// 获取栈顶元素
s.pop();//弹出栈顶元素
}
return 0;
}
3 队列(queue)
3.1 什么是队列
queue: 就是“队列”。
队列是先进先出的(First in first out), 队头的访问和删除操作
只能在队头进行,添加操作只能在队尾进行。不能访问队列中间的元素。
特点:先进先出 (FIFO), 队尾进,队头出。
3.2 队列的常见函数
函数名 | 函数说明 |
---|---|
push(元素) | 进队,从队尾入队 |
pop() | 出队,从队头出队 |
front() | 返回队头元素 |
back() | 返回队尾元素 |
size() | 返回队列中元素个数 |
empty() | 判断队列是否为空 |
3.3 队列的存储与遍历
#include<bits/stdc++.h>
using namespace std;
int main() {
// queue<int> q;
// //i入队:从队尾入
// q.push(10);
// q.push(20);
// q.push(30);
// //打印队头、队尾
// cout<<q.front()<<" "<<q.back()<<endl;
// //出队:从队头出
// q.pop();
// cout<<q.front()<<" "<<q.back()<<endl;
queue<int> q;
int x;
while(1) {
cin>>x;
if(x==-1)break;
q.push(x);// 入队
}
//访问队列元素:相当于将队列元素一直弹出,直到队列为空
while(q.empty()==false) {
cout<<q.front()<<endl;
q.pop();// 出队
}
return 0;
}
迭代器
1.1 什么是迭代器
迭代器(iterator): 用来指向、遍历、修改容器元素的变量,类似指针。
A、可遍历STL 容器内全部或部分元素的对象
B、指出容器中的一个特定位置
操作 | 效果 |
---|---|
* | 返回当前位置上的元素值。 |
++ | 将迭代器前进至下一元素 |
==和!= | 判断两个迭代器是否指向同一位置 |
= | 为迭代器赋值(将所指元素的位置赋值过去) |
1.2 迭代器函数
操作 | 效果 |
---|---|
begin() | 返回一个迭代器,指向第一个元素 |
end() | 返回一个迭代器,指向最后一个元素之后 |
#include<bits/stdc++.h>
using namespace std;
int main() {
char s[]="hello stl";
char *p;//&s &s[0]
//p 是一个指向字符数组的指针,相当于一个迭代器
for(p=s;*p!='\0';*p++){
//p 是指针,指向数组的不同位置,*p 是元素
cout<<*p<<"";
}cout<<endl;
int a[]= {10,20,30,40,50};
vector<int> v(a,a+5);
//定义迭代器,命名为 it
vector<int>::iterator it;
//迭代器指向 vector<int> 的首元素
it=v.begin();
(*it)++;
cout<<*it<<" "<<v[1]<<endl;
//利用迭代器循环遍历 vector
cout<<"迭代器遍历 vector:";
for(it=v.begin();it<v.end();it=it+2) {
cout<<*it<<" ";
}
cout<<endl;
//利用迭代器反向遍历 vector
vector<int>::reverse_iterator rit;
cout<<"反向迭代器遍历:";
for(rit=v.rbegin(); rit!=v.rend(); rit++) {
cout<<*rit<<" ";
}
cout<<endl;
return 0;
}
1.3 迭代器分类
常用的迭代器按功能强弱分为: 输入、输出、正向、双向、随机访问五种,这里只介绍常用 的三种。
不同容器的迭代器,其功能强弱有所不同。例如,排序算法需要通过随机访问迭代器来访问 容器中的元素,因此有的容器就不支持排序算法。
A、 正向迭代器。
假 设 p 是一个正向迭代器,则 p 支持以下操作:++p,p++,*p。
此外,两个正向迭代器可以互相赋值,还可以用=和!=运算符进行比较。
B、 双向迭代器。
双向迭代器具有正向迭代器的全部功能。
双向迭代器p 支持-p 和 p--, 使得 p 朝和++p 相反的方向移动。
C、 随机访问迭代器。
随机访问迭代器具有双向迭代器的全部功能。
随机访问迭代器p 还支持以下操作:
> p+=i: 使得 p 往后移动 i 个元素。
> p-=i: 使得 p 往前移动 i 个元素。
> p+i: 返 回 p 后面第 i 个元素的迭代器。
> p-i: 返 回 p 前面第 i 个元素的迭代器。
> p[i]: 返回 p 后面第 i 个元素的引用。
> 两个随机访问迭代器 p1、p2 还可以用く、>、<=、>=运算符进行比较。 pl<p2 的含义是: p1 经过若干次(至少一次)++操作后,就会等于 p2。
表 达 式p2-p1 表示迭代器 p2 所指向元素和迭代器 p1 所指向元素的序号差 (p2 和 pl 之间的元素个数减一)。
1.4 不同容器支持的迭代器
容器 | 迭代器类别 |
---|---|
vector | 随机 |
deque | 随机 |
list | 双向 |
set/multiset | 双向 |
map/multimap | 双向 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority queue | 不支持迭代器 |
STL 容器的通用操作
1 与大小相关的操作
size() 一返回当前容器的元素数量
empty() 一判断容器是否为空
max size() 一返回容器能容纳的最大元素数量
2 比较
==,!=,<,<=,>,>=
比较操作两端的容器必须属于同一类型
如果两个容器内的所有元素按序相等,那么这两个容器相等
采用字典式顺序判断某个容器是否小于另一个容器
3 元素操作
insert(pos,e) 一 将元 素e 的拷贝安插于迭代器 pos 所指的位置
erase(beg,end) 一移除[beg,end] 区间内的所有元素
clear()一移除所有元素
4 赋值和交换
swap- 用于交换元素
5 与迭代器(iterator)相关的操作
(容器需支持迭代器)
begin()一返回一个迭代器,指向第一个元素
end()一返回一个迭代器,指向最后一个元素之后
rbegin() 一返回一个逆向迭代器,指向逆向遍历的第一个元素
rend()一返回一个逆向迭代器,指向逆向遍历的最后一个元素之后