这篇文章主要会详细讲解栈和队列以及优先级队列的使用以及底层模拟实现,其实早在数据结构我就已经详细讲解了栈和队列以及模拟实现,唯一不同就是这次会用C++来模拟实现。如果不了解栈和队列可以看这篇博客:链接: link
容器适配器
stack
虽然之前我已经详细说明了栈的特性和使用,是一种FILO的结构,我们还是可以再去学习在C++中Stack是如何实例化的。
使用之前包头文件#include <stack>
//往栈内插入4个元素,然后遍历
stack<int> s1;//栈实例化对象的时候必须指定模板参数。
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
while (!s1.empty()) {
cout << s1.top() << endl;
s1.pop();
}
这是栈的成员函数,我给大家介绍几个常用的
empty(); //判断栈是否为空,如果是返回真
size(); //返回栈内元素个数
top(); //返回栈顶元素
push(); //压栈,往栈顶放数据
pop(); //出栈,把栈顶数据移除掉
这是栈常用的一些成员函数,我就不一一演示了。接下来我分析一下栈的模拟实现。
#pragma once
namespace JRG {
//模板
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();
}
void push(T x) {
_con.push_back(x);
}
void pop() {
_con.pop_back();
}
private:
Container _con;
};
}
Stack是一种容器适配器,是借用别的数据结构来完成栈的操作,模板的第二个参数有缺省值,默认是deque,其实vector和list也是可以实现栈的。整个结构设计的非常巧妙,但是却通俗易懂。
queue
队列和栈一样也是容器适配器,是一种FIFO存储形式,也学习一下如何使用。
使用之前包含头文件#include <queue>
//插入四个元素,依次遍历
queue<int, list<int>> q;//在STL库中默认是deque,也可以传vector和list
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty()) {
cout << q.front() << endl;
q.pop();
}
这是queue的成员函数,和栈一模一样,所以不做介绍了。直接看一下模拟实现
#pragma once
namespace JRG {
template<class T, class Container = deque<T>>
class queue {
public:
bool empty() {
return _con.empty();
}
size_t size() {
return _con.size();
}
T& front() {
return _con.front();
}
void push(T x) {
_con.push_back(x);
}
void pop() {
_con.pop_front();
}
private:
Container _con;
};
}
队列的实现也是非常巧妙且一眼能看懂。默认用的数据结构也是deque,也是可以用list和vector。
priority_queue
priority_queue是一种优先级队列,在C语言中是没有的。这个也是一种容器适配器,底层是用堆实现的。
我们可以先见一见优先级队列怎么使用,使用之前包含头文件#include <queue>
。
Priority_Queue<int> pq;
pq.push(11);
pq.push(12);
pq.push(1);
pq.push(4);
pq.push(6);
pq.push(10);
while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
这就是运行结果,每次取堆顶的数据,就可以进行排序,默认是降序排序,如果想要升序排序,需要传入规定的参数,Priority_Queue<int,vector<int>,greater<int>> pq;
greater是升序排序,但是这个参数是第三个,想要传入第三个缺省参数,必须传第二个缺省参数,也就是选用对应的数据结构,优先级队列只能使用vector和deque,list是不可以的。
优先级队列的模拟实现
#pragma once
namespace JRG {
//仿函数决定了升序还是降序
template <class T>
class Greater {//调用Greater就是建小堆
public:
bool operator()(const T& x, const T& y) {
return x < y;
}
};
template <class T>
class Less {//调用这个仿函数默认建小堆
public:
bool operator()(const T& x, const T& y) {
return x > y;
}
};
template<class T,class Container = vector<T>,class Compare = Less<T>>
class Priority_Queue {
public:
//删除元素就是堆顶的元素和最后一个交换,然后把堆顶的元素向下调整,以此来保证构成堆。
void adjustdown(size_t parent) {
Compare com;
size_t child = parent * 2 + 1;
while (child < size()) {
if (child + 1 < size() && com(_con[child+1] , _con[child])) {
child++;
}
if (com(_con[child] , _con[parent])) {
std::swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
void pop() {
std::swap(_con[0], _con[size() - 1]);
_con.pop_back();
adjustdown(0);
}
//添加元素是尾插,尾插时候需要向上调整,以此来构成堆
void adjustup(size_t child) {
Compare com;
size_t parent = (child - 1) / 2;
while (child > 0) {
if (com(_con[child] , _con[parent])) {
std::swap(_con[child], _con[parent]);
child = parent;
parent= (child - 1) / 2;
}
else {
break;
}
}
}
void push(const T& val) {
_con.push_back(val);
adjustup(size() - 1);
}
void swap(Priority_Queue& q) {
std::swap(*this, q);
}
size_t size() {
return _con.size();
}
const T& top() {
return _con[0];
}
bool empty() {
return _con.empty();
}
private:
Container _con;
};
}
这就是优先级队列的底层原理,如果还有不明白堆是怎么建起来的,怎么删除和添加元素的,可以看看这篇文章,里面详细的讲解了堆的底层特点:链接: link
这就是C++中的容器适配器,对您有所帮助的话,给个关注。