栈、队列、优先级队列的模拟实现
优先级队列的模拟实现
- 栈
- 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官方给出的示例&…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...




