当前位置: 首页 > news >正文

stack和queue的使用和模拟实现以及了解deque

目录

  • stack的使用
  • queue的使用
  • 容器适配器
  • stack的模拟实现
  • queue的模拟实现
  • 了解deque(双端队列)
  • vector和deque的排序性能比较

stack的使用

#include <iostream>
#include <stack> // stack的头文件using namespace std;int main()
{// 栈有先进后出的特性stack<int> st;// push 入栈操作st.push(1);st.push(2);st.push(3);st.push(4);while (!st.empty()) // empty 判空{// top 取栈顶元素cout << st.top() << " ";st.pop();// 出栈操作}cout << endl;return 0;
}

queue的使用

#include <iostream>
#include <queue>using namespace std;int main()
{// 队列有先进先出的特性queue<int> q;// push 入队列操作 - 队尾入q.push(1);q.push(2);q.push(3);q.push(4);while (!q.empty()) // empty 判空{// front 取队首元素cout << q.front() << " ";q.pop(); // pop 出队列操作 - 队首出}cout << endl;return 0;
}

容器适配器

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

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

  1. 适配器模式
    把已有的东西封装起来,转换出你想要的东西。
  2. 迭代器模式
    不暴露底层实现细节,封装后提供统一的方式访问容器。

如果我们还是按照以往的想法去实现一个栈,肯定是申请一个变长数组,一个size,一个capacity,再写一些成员函数。

template<class T>
class stack
{
public://成员函数
private:T* _a;size_t _size;size_t _capacity;
};

但是stack,queue都是适配器模式,我们可以不用自己写,而是用已有的东西封装起来,转换成自己想要的东西。

stack的模拟实现

#pragma once
#include <stack>
#include <queue>
#include <list>
#include <deque> // 双端队列namespace bs
{// 适配器/配接器// 容器适配器(container adaptors)template<class T, class Container = deque<T>>class stack{public:void push(const T& val){_con.push_back(val);}void pop(){_con.pop_back();}size_t size() const{return _con.size();}bool empty() const{return _con.empty();}const T& top() const{return _con.back();}T& top(){return _con.back();}private:Container _con;};
}
#include "stack.h"int main()
{// 用 vector 适配//bs::stack<int, vector<int>> st;// 用 list 适配//bs::stack<int, list<int>> st;// 默认用deque(双端队列)适配bs::stack<int> st;st.push(1);st.push(2);st.push(3);st.push(4);while (!st.empty()){cout << st.top() << " ";st.pop();}cout << endl;return 0;
}

queue的模拟实现

#pragma once
#include <stack>
#include <queue>
#include <list>
#include <deque> // 双端队列namespace bs
{// 适配器/配接器// 容器适配器(container adaptors)template<class T, class Container = deque<T>>class queue{public:void push(const T& val){_con.push_back(val);}void pop(){// _con.erase(_con.begin());_con.pop_front(); // vector没有pop_front接口}size_t size() const{return _con.size();}bool empty() const{return _con.empty();}const T& front() const{return _con.front();}T& front(){return _con.front();}const T& back() const{return _con.back();}T& back(){return _con.back();}private:Container _con;};
}
#include "queue.h"int main()
{bs::queue<int> q;q.push(1);q.push(2);q.push(3);q.push(4);while (!q.empty()){cout << q.front() << " ";q.pop();}cout << endl;return 0;
}

了解deque(双端队列)

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

为什么选择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不仅效率高,而且内存使用率高。

结合了deque的优点,而完美的避开了其缺陷。

vector和deque的排序性能比较

vector和deque直接排序比较

#include <iostream>
#include <stack>
#include <queue>
#include <list>
#include <algorithm>using namespace std;void test_op1()
{srand(time(0));const int N = 1000000;deque<int> dq;vector<int> v;for (int i = 0; i < N; ++i){auto e = rand() + i;v.push_back(e);dq.push_back(e);}int begin1 = clock();sort(v.begin(), v.end());int end1 = clock();int begin2 = clock();sort(dq.begin(), dq.end());int end2 = clock();printf("vector:%d\n", end1 - begin1);printf("deque:%d\n", end2 - begin2);
}int main()
{test_op1();return 0;
}

在这里插入图片描述

“deque直接排序”和“deque拷贝到vector再排序再拷贝回来”比较

#include <iostream>
#include <stack>
#include <queue>
#include <list>
#include <algorithm>using namespace std;void test_op2()
{srand(time(0));const int N = 1000000;deque<int> dq1;deque<int> dq2;for (int i = 0; i < N; ++i){auto e = rand() + i;dq1.push_back(e);dq2.push_back(e);}int begin1 = clock();sort(dq1.begin(), dq1.end(), less<int>());int end1 = clock();int begin2 = clock();// vectorvector<int> v(dq2.begin(), dq2.end());sort(v.begin(), v.end());dq2.assign(v.begin(), v.end());int end2 = clock();printf("deque sort:%d\n", end1 - begin1);printf("deque copy vector sort, copy back deque:%d\n", end2 - begin2);
}int main()
{test_op2();return 0;
}

在这里插入图片描述

在这里插入图片描述

http://www.xdnf.cn/news/1106371.html

相关文章:

  • Java基础:泛型
  • 以数据为核心,以业务为导向,漫谈数据可视化应用
  • Leet code 每日一题
  • 【LeetCode】算法详解#8 ---螺旋矩阵
  • 粒子滤波|粒子滤波的相关算法理论介绍
  • 引入了模块但没有使用”,会不会被打包进去
  • STP生成树划分实验
  • 智能制造——解读50页智能工厂系统集成总体解决方案【附全文阅读】
  • Capsule Networks:深度学习中的空间关系建模革命
  • XML 指南
  • 每日一SQL 【 超过 5 名学生的课】
  • TCP的socket编程
  • 【学习新知识】用 Clang 提取函数体 + 构建代码知识库 + AI 问答系统
  • 【Modern C++ Part10】Prefer-scoped-enum-to-unscoped-enums
  • 【Java八股文总结 — 包学会】(二)计算机网络
  • ntfs - SELinux
  • Gas and Gas Price
  • 【Luogu】每日一题——Day1. P3385 【模板】负环
  • 上位机知识篇---高效下载安装方法
  • Script Error产生的原因及解法
  • 机器学习详解
  • Day58
  • Java基础-String常用的方法
  • 隆重介绍 Xget for Chrome:您的终极下载加速器
  • Linux入门篇学习——Linux 编写第一个自己的命令,make 工具和 makefile 文件
  • 嵌入式八股文之 GPIO
  • 鸿蒙系统安全机制全解:安全启动 + 沙箱 + 动态权限实战落地指南
  • 【驱动】移植CH340驱动,设置 udev 规则,解决和 BRLTTY 的冲突
  • Word表格默认格式修改成三线表,一劳永逸,提高生产力!
  • FREERTOS根本不能使用连续接收串口思想