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

【C++从0到王者】第十七站:手把手教你写一个stack和queue及deque的底层原理

文章目录

  • 一、stack
    • 1.利用适配器
    • 2.栈的实现
  • 二、queue
  • 三、deque
    • 1.deque介绍
    • 2.deque的接口
    • 3.deque的基本使用
    • 4.deque的效率
    • 5.deque的原理

一、stack

1.利用适配器

我们不可能写了一份数组栈以后,还要在手写一个链式栈,这样显得太冗余了。于是我们可以利用适配器,传递一个我们想要使用的类型。这样我们的栈就可以做到数组栈和链式栈的秒切换了。从我们用的角度来说并没有太大差别,但是底层早已大变样了。

	template<class T, class Container>class stack{public:private:Container _con;};

2.栈的实现

有了上面的思路,我们就可以很容易的完成栈的接口

#pragma once
#include<vector>
#include<list>
namespace Sim
{template<class T, class Container = vector<T>>class stack{public:void push(const T& val){_con.push_back(val);}void pop(){_con.pop_back();}const T& top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};void test_stack(){stack<int> st1;stack<int, list<int>> st2;st1.push(1);st1.push(2);st1.push(3);st1.push(4);while (!st1.empty()){cout << st1.top() << " ";st1.pop();}cout << endl;}
};

在这里插入图片描述

二、queue

如下所示,是queue的模拟实现,需要注意的是,queue是不可以用vector进行适配的,因为vector并未提供pop_front接口,但是如果想要强制适配的话也是可以的,使用erase接口即可

#pragma once
#pragma once
#include<vector>
#include<list>
namespace Sim
{template<class T, class Container = list<T>>class queue{public:void push(const T& val){_con.push_back(val);}void pop(){_con.pop_front();}const T& front(){return _con.front();}const T& back(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};void test_queue(){queue<int> q1;q1.push(1);q1.push(2);q1.push(3);q1.push(4);while (!q1.empty()){cout << q1.front() << " ";q1.pop();}cout << endl;}
};

在这里插入图片描述

三、deque

1.deque介绍

虽然我们上面使用的适配器缺省参数都是vector或者list,但是我们会发现,库里面的stack和list它的适配器都是deque。deque听名字好像是个队列,名字是双端队列。但是队列是有先进先出的特性的,它不是那么特别符合队列。

在这里插入图片描述
Deque(通常发音像“deck”)是双端队列的不规则缩写。双端队列是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩。

特定的库可能以不同的方式实现deque,通常是某种形式的动态数组。但在任何情况下,它们都允许通过随机访问迭代器直接访问单个元素,并根据需要通过扩展和收缩容器来自动处理存储。

因此,它们提供了类似于向量的功能,但在序列的开始,而不仅仅是在序列的末尾,也可以有效地插入和删除元素。但是,与vector不同,deque不能保证将其所有元素存储在连续的存储位置:通过偏移指向另一个元素的指针来访问deque中的元素会导致未定义的行为。

vector和deque都提供了非常相似的接口,可以用于类似的目的,但两者在内部的工作方式却完全不同:vector使用单个数组,偶尔需要为增长重新分配,而deque的元素可以分散在不同的存储块中,容器内部保留必要的信息,以便在恒定时间内使用统一的顺序接口(通过迭代器)直接访问其任何元素。因此,deque在内部比vector更复杂,但这使得它们在某些情况下更有效地增长,特别是对于非常长的序列,重新分配变得更加昂贵。

对于涉及频繁插入或删除除开始或结束位置以外的元素的操作,deque的性能更差,迭代器和引用的一致性也不如列表和前向列表。

2.deque的接口

如下所示,是deque的接口,我们可以发现,它似乎同时具有list和vector的接口。而且它的迭代器还是随机迭代器。
在这里插入图片描述

deque的接口像是vector和list的合体。但是它看似很强,实际上效率不是很高。单论头插头删,尾插尾删效率还是不错的,但是综合性不是很好。

3.deque的基本使用

void test_deque()
{deque<int> dq;dq.push_back(1);dq.push_back(2);dq.push_back(3);dq.push_back(4);for (int i = 0; i < dq.size(); i++){cout << dq[i] << " ";}cout << endl;

我们可以得知
在这里插入图片描述

4.deque的效率

我们在前面说过,deque的综合效率是不高的。我们可以用下面的代码来看出

void test_op()
{srand(time(0));const int N = 1000000;vector<int> v1;vector<int> v2;v1.reserve(N);v2.reserve(N);deque<int> dq1;deque<int> dq2;for (int i = 0; i < N; ++i){auto e = rand();//v1.push_back(e);//v2.push_back(e);dq1.push_back(e);dq2.push_back(e);}// 拷贝到vector排序,排完以后再拷贝回来int begin1 = clock();// 先拷贝到vectorfor (auto& e : dq1){v1.push_back(e);}// 排序sort(v1.begin(), v1.end());// 拷贝回去size_t i = 0;for (auto& e : dq1){e = v1[i++];}int end1 = clock();int begin2 = clock();sort(dq2.begin(), dq2.end());int end2 = clock();printf("deque copy vector sort:%d\n", end1 - begin1);printf("deque sort:%d\n", end2 - begin2);
}

在这里插入图片描述

deque效率慢的原因主要就是因为它的随机访问[]的效率太低

5.deque的原理

我们知道:

  1. 对于数组,可以下标随机访问,但是存在扩容问题,中间和头部插入效率低下
    在这里插入图片描述

  2. 对于链表,任意位置插入删除效率合适,按需申请释放,但是不支持随机访问

在这里插入图片描述

而现在,我们使用的deque的结构是这样,它是一段一段的开空间,每段空间都是一样大的,然后通过一个中控数组(指针数组)进行连接起来。想要扩容就在连接一块空间即可。当指针数组满了,就中控数组扩容即可。这样一来扩容的代价就很低。不需要拷贝原来的数组。对于头插尾插也很简单,就用专门的两个空间进行头插尾插即可

在这里插入图片描述

它相比vector极大的缓解了扩容、头插头删问题。但是它的[]运算符不够极致。它的[]需要计算在哪个buff数组,在哪个buff数组的第几个。如果我们想要使用它的[]运算符,它内部的逻辑会经历一下几个步骤

  1. 先看在不在第一个buff数组里面,如果在,就直接访问
  2. 不在第一个buff数组里面,i-=第一个buff数组的size
  3. 第几个buff=i/buff.size()
  4. 在这个buff的第几个=i%buff.size()

它相比list,可以支持随机访问,cpu高速缓存访问效率不错,头插尾插删除不错,但是中间位置插入删除效率低下。因为我们需要扩容或者挪动buff的数据。无论哪一种,效率都很低。

根据deque的底层原理,其实对于高频的头插头删,尾插尾删来说,deque还很适合,所以deque用于适配stack和queue来说是很合适的,因为它们只涉及到头部和尾部的插入删除,不涉及中间位置的插入删除

实际上在库里面的deque是更加复杂的,它的迭代器由四个指针组成,这使得deque更加复杂,首先由node指向中控,即指向当前的buff数组,cur指向当前buff数组中的某个数据,first和last指向当前数组的头和尾
在这里插入图片描述

在这里插入图片描述


好了,本期内容就到这里了
如果对你有帮助的话,不要忘记点赞加收藏哦!!!

相关文章:

【C++从0到王者】第十七站:手把手教你写一个stack和queue及deque的底层原理

文章目录 一、stack1.利用适配器2.栈的实现 二、queue三、deque1.deque介绍2.deque的接口3.deque的基本使用4.deque的效率5.deque的原理 一、stack 1.利用适配器 我们不可能写了一份数组栈以后&#xff0c;还要在手写一个链式栈&#xff0c;这样显得太冗余了。于是我们可以利…...

ffmpeg.c源码与函数关系分析

介绍 FFmpeg 是一个可以处理音视频的软件&#xff0c;功能非常强大&#xff0c;主要包括&#xff0c;编解码转换&#xff0c;封装格式转换&#xff0c;滤镜特效。FFmpeg支持各种网络协议&#xff0c;支持 RTMP &#xff0c;RTSP&#xff0c;HLS 等高层协议的推拉流&#xff0c…...

GD32F103待机模式与唤醒

GD32F103待机模式与唤醒&#xff0c;本程序使用RTC报警唤醒。 电源管理单元有3种省电模式:睡眠模式,深度睡眠模式和待机模式&#xff1b; 进入待机模式的步骤如下&#xff1a; 若需要RTC闹钟输出&#xff0c;则需要将TAMPER-RTC映射到PC13引脚; 若需要LXTAL晶振32.768KHz&…...

【Linux初阶】基础IO - 动静态库 | 初识、生成、链接、加载

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;动静态库初识&#xff0c;库的含义&#xff0c;静态库的生成与链接&#xff0c;gcc/g默认链接方式&#xff0c…...

为Git仓库设置签名信息

前言 在首次使用git版本库或创建新的仓库时&#xff0c;需要为其仓库设定管理员和管理员邮箱。 在为仓库添加管理员和邮箱地址时&#xff0c;有以下两种情况&#xff1a; &#xff08;1&#xff09;全局模式&#xff1a;首次创建&#xff0c;后面做为默认使用&#xff0c;对当…...

iOS开发Swift开发UI页面链式调用库推荐

首页链接 https://github.com/zhiguangqiao/ChainableUIKit 安装方法 pod ChainableUIKit调用片段 UIButton import ChainableUIKitprivate let button UIButton().chain.setTitleColor(.init(hex: "#9583EB"), state: .normal).setTitle("全部视频",…...

ClickHouse SQL与引擎--基本使用(一)

1.查看所有的数据库 show databases; 2.创建库 CREATE DATABASE zabbix ENGINE Ordinary; ATTACH DATABASE ck_test ENGINE Ordinary;3.创建本地表 CREATE TABLE IF NOT EXISTS test01(id UInt64,name String,time UInt64,age UInt8,flag UInt8 ) ENGINE MergeTree PARTI…...

2023-08-07力扣今日七题-好题

链接&#xff1a; 剑指 Offer 11. 旋转数组的最小数字 154. 寻找旋转排序数组中的最小值 II 题意&#xff1a; 找一个数组里的最小值&#xff0c;这个数组是有非递减数组旋转而来的&#xff0c;旋转n次表示把前n个数移动到数组末尾 解&#xff1a; 很有趣的二分&#xff…...

支持多用户协同的思维导图TeamMapper

什么是 TeamMapper &#xff1f; TeamMapper 是基于 Mindmapp 开发的用于绘制思维导图的 Web 应用程序。它使得思维导图变得简单&#xff0c;你可以托管并创建您自己的思维导图。与您的团队分享您的思维导图会议并在思维导图上进行协作。 软件特点&#xff1a; 创建&#xff1…...

【Vue】Parsing error: No Babel config file detected for ... vue

报错 Parsing error: No Babel config file detected for E:\Study\Vue网站\实现防篡改的水印\demo02\src\App.vue. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files.             …...

2023-08-07力扣今日五题

链接&#xff1a; 剑指 Offer 53 - II. 0&#xff5e;n-1中缺失的数字 题意&#xff1a; 如题 解&#xff1a; 长度n的递增数组里&#xff0c;要找0到n中没出现的那个数字&#xff0c;那么出现的下标是0到n-1&#xff0c;一一对应即可&#xff0c;都出现了就是n没有 实际…...

ETHERCAT转PROFIBUS连接到300plc的配置方法

由于捷米JM-DP-ECT&#xff0c;是自主研发的一款PROFIBUS从站功能的通讯网关&#xff0c;它的主要功能是将ETHERCAT设备接入到PROFIBUS网络中生产环境比较复杂有多个设备采用不同的协议这极大的阻碍了&#xff0c;各个设备的数据互通。 JM-DP-ECT这个小小的网关可不简单&#x…...

Spring Boot配置文件与日志文件

1. Spring Boot 配置文件 我们知道, 当我们创建一个Spring Boot项目之后, 就已经有了配置文件存在于目录结构中. 1. 配置文件作用 整个项目中所有重要的数据都是在配置文件中配置的&#xff0c;比如: 数据库的连接信息 (包含用户名和密码的设置) ;项目的启动端口;第三方系统的调…...

可解释性分析的一些类别(草稿)(视觉)

目录 1.交互性解释 2. 本身具有解释性的模型 3.如何将可解释性分析应用到生成模型 参考文献 视觉领域从2020年开始可以分为两块&#xff0c;一个是图像分类&#xff0c;一个是图像生成。 图像分类&#xff1a;输入一张图片&#xff0c;输出语义标签&#xff0c;就是这张图…...

HTTPS-RSA握手

RSA握手过程 HTTPS采用了公钥加密和对称加密结合的方式进行数据加密和解密 RSA握手是HTTPS连接建立过程中的一个关键步骤&#xff0c;用于确保通信双方的身份验证和生成对称加密所需的密钥 通过RSA握手过程&#xff0c;客户端和服务器可以协商出一个共享的对称密钥&#xff0c;…...

bigemap国土管理行业应用

由于国营企业单位&#xff0c;管理土地&#xff0c;必须要有这样的软件套图 客户之前用的谷歌&#xff0c;后来不能访问了&#xff0c;通过其他途径搜索到我们 客户使用软件一般都用于套坐标以及空间规划图&#xff0c;方便于项目选址和居民建房报建在卫星图上找到用地范围&am…...

深入探索 Splashtop Enterprise 的潜力

在当今高度技术化的环境中&#xff0c;远程访问解决方案已成为无数组织的支柱。远程访问解决方案缩短了员工与工作之间的地理差距&#xff0c;提高了工作的效率和灵活性&#xff0c;促进形成了无缝的工作体验。在众多远程访问解决方案中&#xff0c;Splashtop Enterprise 作为远…...

创建型模式-单例模式

文章目录 一、创建型模式1. 单例设计模式1.1 单例模式的结构1.2 单例模式的实现&#xff08;1&#xff09;饿汉式-方式1&#xff08;静态变量方式&#xff09;&#xff08;2&#xff09;饿汉式-方式2&#xff08;静态代码块方式&#xff09;&#xff08;3&#xff09;懒汉式-方…...

2. Linux安装Git

yum安装 查看版本 版本太低&#xff0c;所以我们采用自己上传编译的方式进行 删除已安装的git yum remove git 下载最新安装包&#xff0c;并上传到服务器文件夹下 上传&#xff0c;解压 5.安装编译需要的依赖 yum install curl-devel expat-devel gettext-devel openssl-…...

检查网站是HTTP那种协议与获取域名的ipv6地址

前言 最近在做HTTPS的应用&#xff0c;可能需要使用ipv6的地址做SLB&#xff0c;但是怎么检查配置正确&#xff0c;总不能每次都看日志吧&#xff0c;实际上客户端也很容易查看&#xff0c;总结工作经验。 检查HTTP协议版本 笔者想到了使用浏览器方式&#xff0c;或者抓包&a…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...