C++之stack 和 queue

禁默 2024-09-30 17:35:02 阅读 95

目录

前言

1.stack的介绍和使用

1.1 stack的介绍

1.2 stack的使用

1.3 stack 的模拟

2. queue的介绍和使用

2.1 queue的介绍

 2.2 queue的使用

2.3 queue的模拟

3.适配器

3.1 什么是适配器

3.2 STL标准库中stack和queue的底层结构

3.3 deque 的介绍(了解)

3.3.1 deque的原理

3.3.2 deque 的缺陷

3.4 STL标准库中对于stack和queue的模拟实现

3.4.1 stack 的模拟

3.4.2 queue 的模拟

测试代码参考

结束语


前言

在数据结构部分我们通过C实现了栈和队列,本节我们将了解C++版本下的

stack和queue。我们将会很轻松的学习这部分知识。

1.stack的介绍和使用

1.1 stack的介绍

 后进先出

1.2 stack的使用

函数加接口说明  函数加接口说明
stack()    构造空的栈    empty()检测stack是否为空
size()返回stack中元素的个数   top()返回栈顶元素的引用
push()将元素val压入stack中    pop()将stack中尾部的元素弹出 

<code>void test_stack() {

stack<int>s1;

s1.push(1);

s1.push(2);

s1.push(3);

s1.push(4);

s1.push(5);

cout << s1.size() << endl;

cout << s1.top() << endl;

s1.pop();

cout << s1.top() << endl;

}

1.3 stack 的模拟

stack我们是先进先出,并从栈的接口中可以看出,栈实际是一种特殊的vector,因此使用vector完全可以模拟实现stack。

我们将vector设为私有成员

#include <vector>

namespace my_stack {

template <class T>

class stack {

public:

stack() {

}

void push(const T& x) {

c.push_back(x);

}

void pop() {

c.pop_back();

}

const T& top() {

return c.back();

}

size_t size()const {

return c.size();

}

bool empty()const {

return c.empty();

}

private:

std::vector<T> c;

};

}

这样子实现有个缺陷,如果我们需要实现一个链式栈就无法,可能会想到改成list,但是很麻烦。

我们可以导入一个容器类来进行实现。在后面的适配器我们会讲到。

2. queue的介绍和使用

2.1 queue的介绍

 

1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元 素,另一端提取元素。

2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供 一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。

3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。

该底层容器应至少 支持以下操作:

empty:检测队列是否为空    size:返回队列中有效元素的个数

front:返回队头元素的引用   back:返回队尾元素的引用

push_back:在队列尾部入队列    pop_front:在队列头部出队列

4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。 

 2.2 queue的使用

<code>void test_queue() {

queue<int>q1;

q1.push(1);

q1.push(2);

q1.push(3);

q1.push(4);

q1.push(5);

cout << q1.size() << endl;

cout << q1.front() << endl;

cout << q1.back() << endl;

q1.pop();

cout << q1.front() << endl;

}

2.3 queue的模拟

因为queue的接口中存在头删和尾插,因此使用vector来封装效率太低,故可以借助list来模拟实 现queue。

#include <list>

namespace my_queue {

template <class T>

class queue {

public:

queue() {

}

void push(const T& x) {

c.push_back(x);

}

void pop() {

c.pop_back();

}

T& back() {

return c.back();

}

T& front() const

return c.front();

}

size_t size()const {

return c.size();

}

bool empty()const {

return c.empty();

}

private:

std::list<T> c;

};

}

通过官方的queue和stack我们可以看到,在类模版中都定义了一个class Container=deque<T>;

所以我们同样类似这样子写,在后续我们会讲解deque的知识。

namespace my_stack {

template <class T,class Container=vector<T>>

class stack {

public:

stack() {

}

void push(const T& x) {

con.push_back(x);

}

void pop() {

con.pop_back();

}

const T& top() {

return con.back();

}

size_t size()const {

return con.size();

}

bool empty()const {

return con.empty();

}

const T& front() const

{

return con.front();

}

const T& back() const

{

return con.back();

}

private:

Container con;

};

}

这样我们既可以写顺序栈也可以链式栈

my_stack::stack<int,vector<int>>s1;

my_stack::stack<int,list<int>>s1;

这里我们使用了vector来当默认参数,而在方法中却穿插了链表的方法,编译时却没有报错,这是因为这是类模版,在主函数类模板实例化时,按需实例化,使用哪些成员函数就实例化哪些,不会全实例化,所以当我们使用vector的时候,不调用list的方法就行了

#include <list>

namespace my_queue {

template<class T, class Container = list<T>>

class queue

{

public:

void push(const T& x)

{

_con.push_back(x);

}

void pop()

{

_con.pop_front();

}

const T& front() const

{

return _con.front();

}

const T& back() const

{

return _con.back();

}

size_t size() const

{

return _con.size();

}

bool empty() const

{

return _con.empty();

}

private:

Container _con;

};

}

3.适配器

3.1 什么是适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设 计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

3.2 STL标准库中stack和queue的底层结构

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为 容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认 使用deque,比如:

3.3 deque 的介绍(了解)

3.3.1 deque的原理

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端 进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与 list比较,空间利用率比较高。

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个 动态的二维数组,其底层结构如下图所示:

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问 的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示: 

那deque是如何借助其迭代器维护其假想连续的结构呢? 如下图所示

3.3.2 deque 的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩 容时,也不需要搬移大量的元素,因此其效率是必vector高的。

与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。

但是,deque不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其 是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实 际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,

而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构 

为什么选择deque作为stack和queue的底层默认容器 

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性 结构,都可以作为stack的底层容器,比如vector和list都可以;

queue是先进先出的特殊线性数据 结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如 list。

但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进 行操作。

2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的 元素增长时,deque不仅效率高,而且内存使用率高。

3.4 STL标准库中对于stack和queue的模拟实现

3.4.1 stack 的模拟

<code>#include<deque>

namespace my_stack

{

template<class T, class Con = deque<T>>

//template<class T, class Con = vector<T>>

//template<class T, class Con = list<T>>

class stack

{

public:

stack() {}

void push(const T& x){

_c.push_back(x); }

void pop() {

_c.pop_back(); }

T& top() {

return _c.back(); }

const T& top()const {

return _c.back(); }

size_t size()const {

return _c.size(); }

bool empty()const {

return _c.empty(); }

private:

Con _c;

};

}

3.4.2 queue 的模拟

#include<deque>

#include <list>

namespace my_queue

{

template<class T, class Con = deque<T>>

//template<class T, class Con = list<T>>

class queue

{

public:

queue() {}

void push(const T& x) {

_c.push_back(x); }

void pop() {

_c.pop_front(); }

T& back() {

return _c.back(); }

const T& back()const {

return _c.back(); }

T& front() {

return _c.front(); }

const T& front()const {

return _c.front(); }

size_t size()const {

return _c.size(); }

bool empty()const {

return _c.empty(); }

private:

Con _c;

};

}

测试代码参考

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

#include <stack>

#include <queue>

using namespace std;

#include "stack.h"

#include "queue.h"

void print() {

}

void test_stack() {

stack<int>s1;

s1.push(1);

s1.push(2);

s1.push(3);

s1.push(4);

s1.push(5);

cout << s1.size() << endl;

cout << s1.top() << endl;

s1.pop();

cout << s1.top() << endl;

}

void test_queue() {

queue<int>q1;

q1.push(1);

q1.push(2);

q1.push(3);

q1.push(4);

q1.push(5);

cout << q1.size() << endl;

cout << q1.front() << endl;

cout << q1.back() << endl;

q1.pop();

cout << q1.front() << endl;

}

int main() {

//test_stack();

//test_queue();

/*

my_stack::stack<int,list<int>>s1;

s1.push(1);

s1.push(2);

s1.push(3);

s1.push(4);

s1.push(5);

cout << s1.size() << endl;

cout << s1.top() << endl;

s1.pop();

cout << s1.top() << endl;

cout << s1.back() << endl;

cout << s1.front() << endl;

*/

my_queue::queue<int> q1;

q1.push(1);

q1.push(2);

q1.push(3);

q1.push(520);

q1.push(1314);

cout << q1.back() << endl;

cout << q1.front() << endl;

q1.pop();

cout << q1.back() << endl;

cout << q1.front() << endl;

my_stack::stack<int> q2;

q2.push(1);

q2.push(2);

q2.push(3);

q2.push(520);

q2.push(1314);

cout << q2.top() << endl;

cout << q2.top()<< endl;

q2.pop();

cout << q2.top() << endl;

const my_stack::stack<int>& crefMyStack = q2;

cout << "Top element (const) is: " << crefMyStack.top() << endl;

return 0;

}

结束语

本次博客内容就到此结束了。理所当然C++下的stack和queue的实现更加的简便和多种多样!

最后感谢各位友友们的捧场和支持,给小编留个赞吧!!!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。