容器适配器stack,queue和priority_queue
Posted 两片空白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了容器适配器stack,queue和priority_queue相关的知识,希望对你有一定的参考价值。
目录
一.什么是容器适配器
容器适配器是一个封装了序列容器的一个类模板,它在一般的序列容器的基础上提供了一些不同的功能。之所以称为容器适配器,是因为它是适配容器来提供其它不一样的功能。通过对应的容器和成员函数来实现我们需要的功能。
下面介绍了三个容器适配器:statk<T>,queue<T>,priority_queue<T>。
注意:容器适配器都不存在迭代器。
二.stack栈
2.1 stack的介绍
- stack是一种后进先出特点的容器适配器。其删除和插入操作只能在容器的一端进行。即只能在栈顶插入和弹出。
- stack的底层容器可以是任何标准的容器类模板或者其它特定的类容器(你设计的容器),但是这些容器必须支持以下操作:empty(判空),back(获取尾部元素),push_back(尾插),pop_back(尾删),size(获取元素大小)。
- vector,list,deque皆可作为stack的底层容器,默认情况下,如果没有为stack指定容器,默认使用deque作为默认容器。
2.2 stack的使用
函数名称 | 功能 |
stack() | 构造一个空栈 |
empty() | 判断栈是否为空,空返回true,非空返回false |
size() | 返回栈元素个数 |
top() | 返回栈顶元素的引用 |
push() | 在栈顶插入元素 |
pop() | 将stack栈顶元素弹出 |
使用stack需要包含头文件#include<stack>,stack不存在迭代器。
2.3 stack的模拟实现
#include<iostream>
#include<deque>
#include<vector>
#include<list>
using namespace std;
namespace my{
template<class T,class Container=deque<T>>
class stack{
public:
bool empty(){
return _con.empty();
}
size_t size(){
return _con.size();
}
T& top(){
return _con.back();
}
const T& top()const{
return back();
}
void push(const T& val){
_con.push_back(val);
}
void pop(){
_con.pop_back();
}
private:
Container _con;
};
void test_stack(){
stack<int> s;
s.push(1);
s.push(2);
s.push(3);
s.push(4);
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
cout << endl;
}
void test_stack1(){
stack<int,list<int>> s;
s.push(1);
s.push(2);
s.push(3);
s.push(4);
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
cout << endl;
}
}
三.queue队列
3.1 queue介绍
- 队列是一种先进先出的容器适配器,其中从容器的一段插入,另一端提取元素。在队尾插入,在对头弹出元素。
- 底层容器是标准容器类模板之一,也可以只用你专门设计的容器类,该容器必须支持empty(判空),front(获取队头元素的引用),back(获取队尾元素引用),push_back(尾插),pop_front(头删),size(获取元素大小)。
- vector,list和deque满足这些要求。默认情况下,没显示给出queue指定容器,默认使用deque。
3.2 queue的使用
函数声明 | 功能介绍 |
queue() | 构造空队列 |
size() | 返回队列中的元素个数 |
empty() | 判断队列是否为空 |
back() | 返回队尾元素引用 |
front() | 返回队头元素引用 |
push() | 在对尾插入元素 |
pop() | 将队头元素弹出 |
使用queue需要包含头文件#include<queue>,queue容器适配器没有迭代器
3.3queue的模拟实现
因为queue存在头删和尾插,因此使用vector容器来封装效率太低,头删需要挪动数据,并且vector并没有提供pop_front(),因为效率太低。
#pragma once
#include<iostream>
#include<deque>
#include<vector>
#include<list>
using namespace std;
namespace my{
template<class T, class Container = deque<T>>
class queue{
public:
//会调用容器构造函数
queue(){};
//注意有隐含this指针
size_t size()const{
return _con.size();
}
bool empty()const{
return _con.empty();
}
T& back(){
return _con.back();
}
T& front(){
return _con.front();
}
const T& back()const{
return _con.back();
}
const T& front()const{
return _con.front();
}
void push(const T& val){
_con.push_back(val);
}
void pop()
{
_con.pop_front();
}
private:
Container _con;
};
void test_queue1(){
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
cout << q.size() << endl;
cout << q.back() << endl;
cout << q.front() << endl;
while (!q.empty()){
cout << q.front() << " ";
q.pop();
}
cout << endl;
}
}
四.priority_queue优先队列
4.1priority_queue介绍
- 优先队列是容器适配器,根据若排序标准,它的第一个元素总是它所包含的元素中优先级最高的。(或许值是最大的,或许是最小的),就像数据结构里的堆。
- 优先队列默认形成大堆
- 优先队列的元素从顶部弹出,顶部是优先级最高的。
- 底层容器可以是任何容器的类模板,也可以是其他特定设计的容器类(自己设计的),容器支持的功能:随机访问operator[],empty,size,front,push_back,pop_front。
- 标准容器vector和deque都可以满足这些需求,没显示定义容器模板时,优先队列默认使用vector。
- 优先队列使用仿函数来控制生成大根堆还是生成小根堆。
简单介绍一下仿函数:
仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。仿函数是一个类,只是使用起来像函数。等下模拟实现时就知道了。
4.2 priority_queue使用
优先队列默认使用vector作为存储数据的容器,在容器的基础上使用堆算法,将vector中的元素调整成一个堆结构。
注意:优先队列就是一个堆,在使用堆的地方都可以使用优先队列。默认生成大根堆,头文件也是#include<queue>
函数 | 功能 |
priority_queue() | 建立一个空优先队列 |
priority_queue(first,last) | 将迭代器first到last之间的元素建堆 |
top | 返回优先级队列中优先级最高元素,即堆顶元素 |
size | 返回优先队列里的元素个数 |
empty | 判断优先队列是否为空 |
push | 往优先队列中插入元素 |
pop | 删除优先级队列中优先级最高元素,即堆顶元素 |
注意:如果在priority_queue中放自定义类型的数据,即在存储数据的容器中放自定义类型数据需要将操作符<和>重载,因为要进行调整称堆。
4.3优先队列的模拟实现
回顾一下堆的插入与删除操作。
堆的插入:在堆尾插入数据,再向上调整成堆。
堆的删除:将堆顶元素和对尾元素交换,再向下调整成堆。
不带仿函数:
调整函数:
#pragma once
#include<iostream>
#include<deque>
#include<vector>
#include<list>
using namespace std;
namespace my{
template<class T,class Container = vector<T>>
class priority_queue{
public:
priority_queue(){};
template<class inputiterator>
priority_queue(inputiterator first, inputiterator last){
while (first != last){
push(*first);
first++;
}
}
bool empty(){
return _con.empty();
}
size_t size(){
return _con.size();
}
T& top(){
return _con.front();
}
const T& top()const{
return _con.front();
}
void push(const T& val){
_con.push_back(val);
Adjustup(_con.size() - 1);
}
void pop(){
swap(_con[0], _con[_con.size() - 1]);
//少一个元素是size减减,交换后,直接删除最后一个元素
_con.pop_back();
Adjustdown(0);
}
private:
void Adjustup(int child){
int parent = (child - 1) / 2;
while (child > 0){
if (_con[child] > _con[parent]){
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else{
break;
}
}
}
void Adjustdown(int parent){
int child = parent * 2 + 1;
//不能减1,如果没有元素size()为0,类型size_t,减1就是最大的数(正数),会进入循环
while ((size_t)child < _con. size() ){
if (child + 1 < _con.size() && _con[child + 1] > _con[child]){
child++;
}
if (_con[child]>_con[parent]){
swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else{
break;
}
}
}
Container _con;
};
void test_priority_queue(){
int array[] = { 8, 4, 6, 2, 10 };
priority_queue<int> pq(array, array + sizeof(array) / sizeof(int));
cout << pq.size() << endl;
while (!pq.empty()){
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
}
}
仿函数:优先队列通过仿函数来实现建立大根堆还是小根堆。
调整建立大根堆还是小根堆,只需要改变调整函数。父亲结点与孩子结点比较时的大于小于号。怎么通过仿函数来实现呢?
调整函数怎么修改:
#pragma once
#include<iostream>
#include<deque>
#include<vector>
#include<list>
using namespace std;
namespace my{
template<class T>
struct less{
bool operator()(const T& left,const T& right){
return left < right;
}
};
template<class T>
struct greater{
bool operator()(const T& left, const T& right){
return left>right;
}
};
template<class T,class Container = vector<T>,class Compare=less<T>>
class priority_queue{
public:
priority_queue(){};
template<class inputiterator>
priority_queue(inputiterator first, inputiterator last){
while (first != last){
push(*first);
first++;
}
}
bool empty(){
return _con.empty();
}
size_t size(){
return _con.size();
}
T& top(){
return _con.front();
}
const T& top()const{
return _con.front();
}
void push(const T& val){
_con.push_back(val);
Adjustup(_con.size() - 1);
}
void pop(){
swap(_con[0], _con[_con.size() - 1]);
//少一个元素是size减减,交换后,直接删除最后一个元素
_con.pop_back();
Adjustdown(0);
}
private:
void Adjustup(int child){
int parent = (child - 1) / 2;
Compare com;
while (child){
if (com(_con[parent],_con[child])){
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else{
break;
}
}
}
void Adjustdown(int parent){
int child = parent * 2 + 1;
//定义一个对象
Compare com;
//不能减1,如果没有元素size()为0,类型size_t,减1就是最大的数(正数),会进入循环
while ((size_t)child < _con. size() ){
if (child + 1 < _con.size() && com(_con[child], _con[child + 1])){
child++;
}
if (com(_con[parent], _con[child])){
swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else{
break;
}
}
}
Container _con;
};
void test_priority_queue(){
int array[] = { 8, 4, 6, 2, 10 };
priority_queue<int,vector<int>,greater<int>> pq(array, array + sizeof(array) / sizeof(int));
cout << pq.size() << endl;
while (!pq.empty()){
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
}
}
以上是关于容器适配器stack,queue和priority_queue的主要内容,如果未能解决你的问题,请参考以下文章
C++入门stack和queue适配器介绍+ priority_queue的模拟实现仿函数基本概念提及
[C/C++]详解STL容器3--stackqueue和priority_queue的功能和模拟实现,deque和容器适配器的介绍