栈、队列、优先级队列的模拟实现
优先级队列的模拟实现
- 栈
- stack的模拟实现
- push()
- pop()
- top()
- size()
- empty()
- swap()
- stack总代码
- 队列
- queue的模拟实现
- push()
- pop()
- front()
- back()
- empty()
- size()
- swap()
- queue总代码
- 优先级队列(堆)
- push()
- pop()
- top()
- empty()
- size()
- swap()
- priority_queue总代码
- deque的了解
栈
在C++STL中栈并不属于容器一类,而属于适配器!
1、栈是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
2、stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定
的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
3、stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下
操作:
empty:判空操作
back:获取尾部元素操作
push_back:尾部插入元素操作
pop_back:尾部删除元素操作
4、标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。
stack的模拟实现
对于栈的话,我们可以在其底层封装一个容器来实现:
template<class T, class Container=std::deque<T>>class stack{private:Container _con;//使用该容器来实现栈,具体使用那个类型的容器,完全由程序员自己决定!默认情况是使用缺省值(deque)双端队列来实现!}
对于stack我们可以不用写构造函数、拷贝构造函数、析构函数、重载赋值运算符!因为stack类的成员变量是个自定义类型,如果我们使用stack的默认构造函数的话,编译器会调用自定义类型的默认构造函数来初始化_con成员变量;同理如果使用默认拷贝构造的话,对于自定义类型也会调用自定义类型的拷贝构造函数;如果使用默认析构函数的话,对于自定义类型也会调用自定义类型的析构函数来释放空间,不会造成内存泄漏;赋值运算符同理!
push()
void push(const T& val)//对于栈的插入,也就是对于容器_con的尾插
{
_con.push_back(val);
}
pop()
void pop()//对于stack的删除就是对于容器_con的尾删除!
{assert(empty() == false);_con.pop_back();
}
top()
T Top()const//获取stack的栈顶元素就是获取容器_con的尾部元素
{assert(empty()==false);return _con.back();
}
size()
size_t size()const//获取stack的元素就是获取容器_con的元素个数
{return _con.size();
}
empty()
bool empty()const//判断stack是不是空就是判断容器_con是不是空
{ return _con.empty();
}
swap()
void swap(stack<T,Container>&st)//交换stack就是交换两个stack内部的容器_con
{_con.swap(st._con);
}
stack总代码
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
#include<list>
#include<deque>
namespace MySpace
{template<class T, class Container=std::deque<T>>class stack{public:explicit stack(const Container&con= Container()):_con(con){}bool empty()const{ return _con.empty();}size_t size()const{return _con.size();}T Top()const{assert(empty()==false);return _con.back();}void push(const T& val){_con.push_back(val);}void pop(){assert(empty() == false);_con.pop_back();}void swap(stack<T,Container>&st){_con.swap(st._con);}private:Container _con;//组成stack的容器};
}
队列
1、 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端
提取元素。
2、队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的
成员函数来访问其元素。元素从队尾入队列,从队头出队列。
3、底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操
作:
empty:检测队列是否为空
size:返回队列中有效元素的个数
front:返回队头元素的引用
back:返回队尾元素的引用
push_back:在队列尾部入队列
pop_front:在队列头部出队列
4、标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。
queue的模拟实现
队列与栈一样不属于容器,而属于适配器;因此队列的底层也是封装的一个容器;
因此队列也不需要自己写默认构造函数、拷贝构造函数、赋值运算符重载、析构函数,我们用编译器默认生成的就够了!
template <class T,class Container=std::deque<T>>//不建议使用vector作为容器,这样的话头插效率低
class queue{private:Container _con;}
push()
void push(const T& val)//对于queue的插入就是对于容器的尾插
{_con.push_back(val);
}
pop()
void pop()//对于queue的删除就是对于容器_con的头删
{assert(empty() == false);_con.erase(_con.begin());
}
front()
T front()const//获取queue队头元素,也就是获取容器_con首元素
{assert(empty() == false);return _con.front();
}
back()
T back()const//获取队尾元素,也就是获取容器_con的尾元素
{assert(empty() == false);return _con.back();
}
empty()
bool empty()const//对于queue的判空,也就是对于容器_con的判空
{return _con.empty();
}
size()
size_t size()const {//对于queue的大小就是容器_con的大小
return _con.size();
}
swap()
void swap(queue<T, Container> &q)//对于queue的交换就是交换底层的两个容器;
{
_con.swap(q._con);
}
queue总代码
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
#include<list>
#include<deque>
namespace MySpace
{template <class T,class Container=std::deque<T>>//不建议使用vector作为容器,这样的话头插效率低class queue{public:explicit queue(const Container& con = Container()):_con(con){}bool empty()const{return _con.empty();}size_t size()const {return _con.size();}T front()const{assert(empty() == false);return _con.front();}T back()const{assert(empty() == false);return _con.back();}void push(const T& val){_con.push_back(val);}void pop(){assert(empty() == false);_con.erase(_con.begin());}void swap(queue<T, Container> &q){_con.swap(q._con);}private:Container _con;};
}
优先级队列(堆)
学过数据结构的读者应该直到数据结构中有一种叫做“堆”的数据结构,在C++中这种数据结构叫做“优先级队列”!关于堆的详细了解可以参考我的另一篇博客:二叉树和堆
优先级队列在STL中也是一个适配器!
因此对于优先级队列的定义我们可以参照stack和queue:
template <class T,class Container=std::vector<int>,class Compare=std::less<T>>
class priority_queue{private:Container _con;}
其中模板参数Container是容器类型;T是元素类型:Compare是仿函数的类型!用于控制建立大堆还是建立小堆;
其中根据STL的实现方法是:优先级队列默认是建大堆,如果需要建小堆的话,Compare模板参数需要传递greater <T>;
push()
优先级队列的push与stack、queue的插入不同,优先级队列push过后还需要调整当前的堆结构,使其再插入一个元素过后仍然保持堆结构,我们通常使用向上调整的算法建队!
void push(const T& val)
{//先插入_con.push_back(val);//向上调整AdjustUp(_con.size()-1);
}void AdjustUp(int child)//向上调整算法{Compare com;//实例化一个比较对象//如果Compare是个小于类的话,那么com实例化出来的就是小于对象,里面的operator(A,B)函数是比较A是否小于B的int parent = (child - 1) / 2;while (child>0){//if (_con[child] > _con[parent])//判断[parent]是否小于[child]if (com(_con[parent], _con[child]))//如果_con[parent]<_con[child]就建立小堆;如果_con[parent]>_con[child]就建立大堆;至于_con[parent]与_con[child]使用<比较还是>比较由我们传递的类型参数Compare来决定!{std::swap(_con[child],_con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}}
pop()
优先级队列删除元素,是删除堆顶的元素,再删除堆顶元素过后,我们仍需要保持该结构是个堆;为了降低调整成本,优先级队列的删除操作的步骤一般都是,交换堆顶元素与堆底元素,然后堆的大小减1,然后再从对顶开始执行向下调整算法调整堆;
void pop()
{assert(empty()==false);//swapstd::swap(_con[0],_con[_con.size()-1]);//size--_con.pop_back();//向下调整AdjustDown(0);
}
void AdjustDown(int parent){Compare com;//实例化一个比较对象int child = 2 * parent + 1;while (child < _con.size()){//if (child + 1 < _con.size() && _con[child + 1] > _con[child])if (child + 1 < _con.size() && com(_con[child],_con[child+1]))child++;//if (_con[child] > _con[parent])if (com(_con[parent],_con[child])){std::swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}elsebreak;}}
top()
const T& top()const//返回堆顶元素,也就死返回容器_con的首元素
{return _con.front();
}
empty()
bool empty()const//判断一个优先级队列是不是空,就是判断一个容器是不是空
{return _con.empty();
}
size()
size_t size()const//求优先级队列的元素个数,就是返回容器的元素个数
{return _con.size();
}
swap()
void swap(priority_queue<T, Container, Compare>&pq)//两个优先级队列之间的交换就是交换底层的容器;
{_con.swap(pq._con);
}
priority_queue总代码
#pragma once
#include<iostream>
#include<vector>
#include<queue>
#include<deque>
#include<assert.h>
namespace MySpace
{template<class T>struct less//小于模板(用于建大堆){bool operator()(const T& v1, const T& v2)//这个函数的目的是为了比较v1是否小于v2{return v1 < v2;}};template<class T>struct less<T*>//小于模板偏特化,如果less模板的模板参数是指针,则用这个版本的less模板实例化对象{bool operator()( T* x, T * y)const{return *x < *y;}};template<class T>struct greater//大于模板(用于建小堆){bool operator()(const T& v1, const T& v2)const//这个函数的目的是为了比较v1是否大于v2{return v1 > v2;}};template<class T>struct greater<T*>//大于模板偏特化,如果greater模板的模板参数为指针类型,那么编译器就用这个版本的greater模板实例化对象!{bool operator()( T* x, T* y){return *x > *y;}};//默认建的是大堆,建大堆用less(小于)//建小堆用greter;template <class T,class Container=std::vector<int>,class Compare=MySpace::less<T>>class priority_queue{public:void push(const T& val){//先插入_con.push_back(val);//向上调整AdjustUp(_con.size()-1);}void pop(){assert(empty()==false);//swapstd::swap(_con[0],_con[_con.size()-1]);//size--_con.pop_back();//向下调整AdjustDown(0);}const T& top()const{return _con.front();}bool empty()const{return _con.empty();}void swap(priority_queue<T, Container, Compare>&pq){_con.swap(pq._con);}size_t size()const{return _con.size();}private:void AdjustDown(int parent){Compare com;//实例化一个比较对象int child = 2 * parent + 1;while (child < _con.size()){//if (child + 1 < _con.size() && _con[child + 1] > _con[child])if (child + 1 < _con.size() && com(_con[child],_con[child+1]))child++;//if (_con[child] > _con[parent])if (com(_con[parent],_con[child])){std::swap(_con[child], _con[parent]);parent = child;child = 2 * parent + 1;}elsebreak;}}void AdjustUp(int child){Compare com;//实例化一个比较对象int parent = (child - 1) / 2;while (child>0){//if (_con[child] > _con[parent])//判断[parent]是否小于[child]if (com(_con[parent], _con[child]))//判断[parent]是否小于[child]{std::swap(_con[child],_con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}}Container _con;//容器};
}
deque的了解
通过前面的学习,我们知道了vector与list的优缺点:
vector:
优点:
1、支持随机访问;
2、尾插尾删效率高;
缺点:
1、中间和头部的插入删除,效率极低!
list
优点:
1、插入删除效率高;
缺点:
2、不支持随机访问;
那么有没有结合vector与list两个容器的优点的数据结构呢?
答案是有的!
双端队列(deque)就出现了:
什么是deque?
deque是一个两端开口,逻辑上空间是连续的数据结构,该数据结构的头部插入删除、尾部插入删除效率极高,时间复杂度为O(1);与list比较,空间利用率比较高!它的每个元素中不需要像list那样存储下一个节点的指针!
同时deque也支持像vector一样用[ ]来访问元素!
但是deque的底层真正实现并不是一块连续的空间,刚才我们也说了deque是在逻辑上连续的空间!真正的deque是由一段一段的连续空间组成起来的,有点类似于C语言中的二维数组!
其底层结构如下:
既然deque这么强,那么为什么还没替代掉vector和list呢?
deque的缺点:
与vector相比,头插头删效率的确高,不需要挪动元素,同时扩容代价下,deque的扩容只需要对中控数组进行扩容,不需要对存储元素的小段数组进行扩容,拷贝过来的也是这些小段数组的指针!
与list相比,空间利用率的确变高了,同时也支持了随机访问;
但是deque有一个致命的缺点:不适合遍历,因为deque迭代器在遍历的时候,会频繁的检测这次遍历是否移动到下一个小段数组中去,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,同时deque特别不适合中间来插入和删除数据,在中间删除和插入数据都需要大量的挪动数据,代价非常大!
而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构;
相关文章:
栈、队列、优先级队列的模拟实现
优先级队列的模拟实现栈stack的模拟实现push()pop()top()size()empty()swap()stack总代码队列queue的模拟实现push()pop()front()back()empty()size()swap()queue总代码优先级队列(堆)push()pop()top()empty()size()swap()priority_queue总代码deque的了解栈 在CSTL中栈并不属…...
JMM内存模型
JMM内存模型JMM内存模型定义三大特性原子性可见性有序性volatile语义JMM规则操作系统实现术语缓存一致性要求缓存一致性机制写传播事务串行化重排序as-if-serial 语义(像是有序的)happens-before 原则happens-before 原则的八大子原则内存屏障总结finalf…...
Linux- 系统随你玩之--玩出花活的命令浏览器-双生姐妹花
文章目录1、背景2、命令浏览器-双生姐妹花2.1、姐妹花简介2.2 、验名正身2.3、常用功能选项3、常用实操3.1、发送请求获取文件3.1.1、抓取页面内容到一个文件中3.1.2、多个文件下载3.1.3、下载ftp文件3.1.4、断点续传3.1.5、上传文件3.1.6、内容输出3.2 、利用curl测试接口3.3 …...
【深度学习】基于Hough变化的答题卡识别(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。⛳座右铭&#…...
Linux - 进程控制(创建和终止)
1.进程创建fork函数初识 在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。返回值:子进程返回0,父进程返回子进程id,出错返回-1getpid()获取子进程id,…...
依赖注入~
依赖注入之setter注入: 依赖注入是IOC具体的一种实现方式, 这是针对资源获取的方式角度来说的,之前我们是被动接受,现在IOC具体的实现叫做依赖注入,从代码的角度来说,原来创建对象的时候需要new࿰…...
【嵌入式硬件芯片开发笔记】HART协议调制解调芯片AD5700配置流程
【嵌入式硬件芯片开发笔记】HART协议调制解调芯片AD5700配置流程 XTAL_EN接地,CLK_CFG的两个引脚由同一个GPIO控制 初始时HART_CLK_CFG输出低电平 由RTS引脚控制调制/解调。当RTS处于高电平时,为解调(输入);否则为调…...
Go语言异步下载视频
异步下载mp4视频列表 下面是一个简单的Go语言示例,用于异步下载视频。我们将使用goroutines来实现异步下载,并使用sync.WaitGroup来等待所有下载任务完成。此示例依赖于net/http包来执行HTTP请求。 package mainimport ("fmt""io"…...
前缀树(字典树/Trie) -----Java实现
目录 一.前缀树 1.什么是前缀树 2.前缀树的举例 二.前缀树的实现 1.前缀树的数据结构 1.插入字符串 2.查找字符串 3.查找前缀 三.词典中最长的单词 1.题目描述 2.问题分析 3.代码实现 一.前缀树 1.什么是前缀树 字典树(Trie树)是一种树形…...
申请专利需要具备什么条件
申请专利需要具备什么条件 在我国,如果创造出来了新的发明都可以申请专利权,一旦申请成功之后,自己的发明就受到了法律的保护,任何人不得以违法的手段进行侵犯。那么申请专利需要具备什么条件?今天律赢时代网就为大家…...
【C++】一篇带你搞懂C++“引用”
前言在C语言的学习中,并没有引用这个概念,但是在C中,加入了引用这个概念,说明引用也是很重要的,但是我们怎么理解引用呢?我是这么理解的,例如在水浒传中,108个英雄好汉都是自己的外号…...
蓝桥杯刷题冲刺 | 倒计时19天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.抓住那头牛2.排列序数1.抓住那头牛 题目 链接: 抓住那头牛 - C语言网 (dotcpp.com…...
Java每日一练(20230321)
目录 1. 出现次数最多的字符 🌟 2. 最后一个单词的长度 🌟 3. 两数之和 🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 出现次数最多的字符并…...
【三维几何学习】从零开始网格上的深度学习-3:Transformer篇(Pytorch)
本文参加新星计划人工智能(Pytorch)赛道:https://bbs.csdn.net/topics/613989052 从零开始网格上的深度学习-3:Transformer篇引言一、概述二、核心代码2.1 位置编码2.2 网络框架三、基于Transformer的网格分类3.1 分类结果3.2 全部代码引言 本文主要内容如下&#…...
一、基础算法3:二分 模板题+算法模板(数的范围,数的三次方根)
文章目录算法模板整数二分算法模板浮点数二分算法模板模板题数的范围原题链接题目题解数的三次方根原题链接题目题解算法模板 整数二分算法模板 bool check(int x) {/* ... */} // 检查x是否满足某种性质// 区间[l, r]被划分成[l, mid]和[mid 1, r]时使用: int b…...
Spring 源码解析 - Bean创建过程 以及 解决循环依赖
一、Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDefinition 存放进了 beanDefinitionMap 容器中,那 Bean 是…...
移除元素(双指针)
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的…...
76.qt qml-QianWindow开源炫酷界面框架(支持白色暗黑渐变自定义控件均以适配)
界面介绍界面支持: 透明 白色 黑色 渐变 单色 静态图 动态图侧边栏支持:抽屉、带折叠、多模式场景控件已集成: 暗黑风格 高亮风格、并附带个人自定义控件及开源demo白色场景如下所示:单色暗黑风格如下所示:用户自定义皮肤如下所示:皮肤预览如下所示:b站入口:https://www.bilibi…...
Python生日蛋糕
目录 前言 底盘 蛋糕 蜡烛 祝福 前言 Hello,小伙伴们晚上好吖!前两天博主满20岁啦(要开始奔三辽呜呜呜),这几天收到了不少小伙伴们的祝福,浪漫的小博主想送给大家一份不一样的生日蛋糕,…...
QT 如何提高 Qt Creator 的编译速度
如何提高编译速度,貌似是一个老生常谈的话题。对于Qter而言,如何提高QT Creator 的编辑速度是一直都是大家所期盼的。本文也是查阅了各路大神的方法后整理出来的,希望对各位有所帮助。 1、在*.pro文件添加预编译机制 QT官方给出的示例&…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...




