容器适配器
容器适配器,可以理解为“容器的容器”,或者说将将不适用的容器变得适用,主要是针对序列式容器。
STL的容器适配器包括三大类:
名称 | 描述 | 可以满足条件的容器 | 默认底层容器 |
stack | 栈,经典的后进先出(LIFO) | deque、list、vector | deque |
queue | 队列,经典的先进先出(FIFO) | deque、list | deque |
priority_queue | 优先队列,会自动排序的队列,遵循"先进,优先级最大的元素先出"的规则 | deque、vector | vector |
Stack
Stack是一种名为栈的数据结构,通常用来存储具有“先进后出”数据结构的数据,其本质仍然是线性表,只不过只允许在表的尾端插入与删除
stack位于<stack>
头文件中,使用需要:
#include<stack>
using namespace std;
API
创建栈及其初始化
1.构造函数
stack<T> s;//创建数据类型为T的栈,T可以是任意数据类型(包括结构),并且底层采用默认的deque
2.修改底层实现的构造函数
stack<T,Container<T>> s;//Container可以是STL中任意实现了栈对应操作的STL容器,这里包括vector、deque和list
3.用基础容器构造
Container<T> cc{value1,value2...};
stack<T,Container<T>> s(cc);
//Container可以是STL中任意实现了栈对应操作的STL容器,这里包括vector、deque和list
//T与Container必须一致
4.拷贝构造函数
stack<T,Container<T>> s;
stack<T,Container<T>> ss(s);
//T与Container必须一致
//way1
stack<int> s;
//way2
stack<int, vector<int>> ss;
//way3
list<int> L{ 1,2,3 };
stack<int, list<int>> sss(L);//注意栈顶是3
//way4
stack<int, vector<int>> s2;
stack<int, vector<int>> s3(s2);
push
把数据元素压入栈
s.push('a');//把字符a压入栈
pop
把数据元素出栈
s.pop();//栈顶出栈,注意栈空时调用该函数会报错
empty
判断栈是否为空
s.empty();//为空则返回true,否则为false
top
返回栈顶元素
s.top();//返回值为栈顶元素
size
返回栈内的元素个数
s.size();//返回栈内的元素个数
emplace
在栈顶直接构造元素
s.emplace('a');//在栈顶直接构造字符a,速度比push快
swap
在两个栈的<T>
与Container<T>
一致的前提条件下交换两个栈
swap(s,ss);
Queue
Queue是一种名为队列的数据结构,通常用来存储具有“先进先出”数据结构的数据,其本质仍然是线性表,只不过只允许在表的一端插入,在表的另外一段删除
队列位于<queue>
头文件中,使用需要:
#include<queue>
using namespace std;
API
创建队列及初始化
1.构造函数
queue<T> q;//创建数据类型为T的队列,T可以是任意数据类型(包括结构),默认底层为deque
2.修改底层实现的构造函数
queue<T,Container<T>> q;//Container可以是STL中任意实现了栈对应操作的STL容器,这里包括deque和list
3.用基础容器构造
Container<T> cc{value1,value2...};
queue<T,Container<T>> q(cc);
//Container可以是STL中任意实现了栈对应操作的STL容器,这里包括vector、deque和list
//T与Container必须一致
4.拷贝构造函数
queue<T,Container<T>> q;
queue<T,Container<T>> qq(q);
//T与Container必须一致
//way1
queue<int> q;
//way2
queue<int, list<int>> qq;
//way3
list<int> L{ 1,2,3 };
queue<int, list<int>> qqq(L);//注意队头是3
//way4
queue<int, list<int>> q2;
queue<int, list<int>> q3(q2);
push
把数据元素压入队列
q.push('a');
pop
把数据元素弹出队列
q.pop();
empty
判断队列是否为空
q.empty();
front
返回队列头元素
q.front();
back
返回队尾元素
q.back();
size
返回队列内元素个数
q.size();
emplace
在队列尾直接创建元素
q.emplace('a');
swap
在两个队列的<T>
与Container<T>
一致的前提条件下交换两个队列
swap(q,qq);
priority_queue
称为优先队列,是一种模拟了队列的数据结构,但是与queue不同,每次只能访问队头
同时,stack和queue在加入元素后不会对其进行排序,都是直接加入末尾,但是优先队列每加入一个新元素都会实现一次排序,将新元素插入排好序的位置中,并且优先队列总是优先级最大的在队头,优先级小的在队尾
因此,优先队列是一种先入,但不一定先出,优先级最大的先出队的结构
因为优先队列与另外两者相比比较特殊,这里给出其定义:
template <typename T,
typename Container=std::vector<T>,
typename Compare=std::less<T> >
class priority_queue{
//......
}
构造一个优先队列需要三个参数,其中2 3个都有默认值,分别为vector和less,前者表示底层采用vector构造,后者表示优先队列采取less(降序)排序,此时队头为最大元素,又称大根堆。与其他STL容器的排序规则一样,这里的排序规则也可以自定义或者使用已封装好的其他排序规则
优先队列位于<queue>
头文件中,使用需要:
#include<queue>
using namespace std;
优先队列的底层实现
priority_queue排序以及存储的实现,得益于堆这一数据结构
堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆 (左图所示) 或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆(右图所示)。
堆这一数据结构具有一个重要性质:根结点一定是整个树中的最大(最小)值对应的结点。
另外,对于同一父节点下的子节点一般不要求次序。
优先队列实现自定义排序
对于非基本数据类型,要想实现插入优先队列,首先就要解决排序问题
方法1:函数对象
typedef struct cmp {//函数对象实现
bool operator ()(const Stu& stu1, const Stu& stu2) const {
if (stu1.age == stu2.age) {
return stu1.name < stu2.name;
}
return stu1.age < stu2.age;
}
};
方法2:重载<或者>,借助less或greater实现,该方法对指针类型不适用,less对应<,greater对应>
bool operator < (const Stu& stu1, const Stu& stu2){//重载<实现less
if (stu1.age == stu2.age) {
return stu1.name < stu2.name;
}
return stu1.age < stu2.age;
}
完整代码:
typedef struct Stu {
string name;
int age;
friend ostream& operator << (ostream& os, const Stu& stu) {
os << stu.name << ' ' << stu.age << endl;
return os;
}
};
typedef struct cmp {//函数对象实现
bool operator ()(const Stu& stu1, const Stu& stu2) const {
if (stu1.age == stu2.age) {
return stu1.name < stu2.name;
}
return stu1.age < stu2.age;
}
};
bool operator < (const Stu& stu1, const Stu& stu2){//重载<实现less
if (stu1.age == stu2.age) {
return stu1.name < stu2.name;
}
return stu1.age < stu2.age;
}
int main() {
priority_queue<Stu,vector<Stu>, cmp> pq;
Stu s[3]{ { "a",1 } ,{"b",2},{"c",1} };
for (int i = 0; i < 3; i++) pq.emplace(s[i]);
while (!pq.empty()) {
cout << pq.top();
pq.pop();
}
priority_queue<Stu> pq2;
for (int i = 0; i < 3; i++) pq2.emplace(s[i]);
while (!pq2.empty()) {
cout << pq2.top();
pq2.pop();
}
}
API
创建优先队列及初始化
1.构造函数
priority_queue<T> q;//创建数据类型为T的队列,T可以是任意数据类型(包括结构),默认底层为deque
2.修改底层实现以及排序规则的构造函数
priority_queue<T,Container<T>,cmp> q;//Container可以是STL中任意实现了栈对应操作的STL容器,这里包括deque和vector
3.用基础容器构造
Container<T> cc{value1,value2...};
priority_queue<T,Container<T>> q(begin,end);
//Container可以是STL中任意实现了栈对应操作的STL容器,这里包括vector、deque
//T与Container必须一致
//注意这里只能使用迭代器或者指针,范围为[begin,end),元素在优先队列中的顺序与插入顺序、原有顺序无关
4.拷贝构造函数
priority_queue<T,Container<T>> q;
priority_queue<T,Container<T>> qq(q);
//T与Container必须一致
//way1
priority_queue<int> pq;
//way2
priority_queue<int, deque<int>,greater<int>> pq2;//需要注意,greater在function头文件中
//way3
deque<int> L{ 1,2,3 };
priority_queue<int> sss(L.begin(),L.end());
//way4
priority_queue<int, vector<int>> s2;
priority_queue<int, vector<int>> s3(s2);
push
往优先队列中加入元素
pq.push(1);
pop
弹出优先队列对头
pq.pop();
empty
判断优先队列是否为空
pq.empty();
size
返回优先队列中的元素个数
pq.size();
emplace
在优先队列正确的位置构造元素
pq.emplace(1);
swap
在两个优先队列的<T>
与Container<T>
和比较规则一致的前提条件下交换两个优先队列
swap(pq1,pq2);