当前位置: 首页 > 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.dtcms.com/a/275363.html

相关文章:

  • 以数据为核心,以业务为导向,漫谈数据可视化应用
  • 网络编程员工管理系统
  • 【MogDB】一种基于ctid分片并发查询以提升大表查询性能的方式
  • RBAC权限模型深度解析:从理论到企业级实践
  • 贪心算法题解——跳跃游戏 II【LeetCode】
  • The Black Heart
  • 飞算 JavaAI 智能编程助手:颠覆编程旧模式,重构新生态
  • 【极客日常】后端任务动态注入执行策略的一种技术实现
  • 27.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--币种服务(一)
  • .net机器学习框架:ML.NET数据处理
  • 【嵌入式硬件实例】-555定时器实现倍压电路
  • 钉钉小程序开发环境配置与前端开发指南
  • 计算机毕业设计ssm基于SSM的萌宠交流平台 基于SSM的毛孩子互动分享与领养系统 SSM架构下的宠物社交·商城·救助一体化平台
  • Mac M芯片安装RocketMQ服务
  • 【Docker基础】Dockerfile指令速览:基础常用指令详解
  • 【STM32实践篇】:最小系统组成
  • 实战指南:用pmap+gdb排查Linux进程内存问题
  • 9.4 自定义SMC服务开发
  • springboot使用@Validated不起作用
  • 加法器学习
  • AI图像修复工具CodeFormer实测:马赛克去除与画质增强效果评测
  • Java使用Langchai4j接入AI大模型的简单使用(四)--整合Springboot
  • Window/linux获得程序运行目录C++
  • C++ Map 和 Set 详解:从原理到实战应用
  • 基于 Python 的数据分析技术综述
  • 2025 全球酒店用品厂家竞争力排行榜发布:扬州卓韵领衔,布草工厂实力重塑行业格局
  • 未来软件开发的新方向:从工程到智能的深度演进
  • 利用scale实现图片放大案例
  • 商业机密保卫战:如何让离职员工带不走的客户资源?
  • TCP的socket编程