STL之Stack与queue的模拟实现与duque的底层结构(3千字长文详解)

news2024/11/18 1:47:38

STL之Stack与queue的模拟实现与duque的底层结构

文章目录

  • STL之Stack与queue的模拟实现与duque的底层结构
    • 设计模式的概念
      • 适配器模式
    • stack的实现
    • queue的实现
    • 双端队列——deque
      • deque的底层结构

设计模式的概念

设计模式像是古代的兵法,是以前的人总结出来的一些在特定的情况下,某种特定的好用的方法总结

STL中迭代器也是一种设计模式——迭代器模式

STL中stack和queue的实现就是使用了一种设计模式——适配器模式!

适配器模式

那么什么叫做适配器模式呢?现实中什么东西可以被叫做适配器?

例如手机充电头!——就是一种电源适配器!我们日常的电源一般都是220v,但是手机一般的充电功率都是几v!如果不使用电源适配器,那么直接充电很容易会让手机坏掉!

所以适配器模式是什么?适配器模式就是一种转换!用已有的东西转换出我们现在想要的东西

上面提到的迭代器模式就是不暴露底层细节,封装后提供同一的方式来访问容器

如果我们使用适配器模式,我们去手动实现一个stack和queue,那么我们就要从头开始进行设计

//例如
template<class T>
class Stack
{
public:
    //里面实现各种函数
private:
    T* _a;
    size_t _size;
    size_t capacity;
}

但是我们其实没有必要做那么多重复的工作!vector已经帮我们实现了很多我们必要的东西!我们可以通过适配器模式的思想将vector转换为stack!(list也可以)

stack的实现

#include <vector>
#include <list>
namespace MySTL
{
	template<class T, class Container = std::vector<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		bool empty()
		{
			return _con.empty();
		}
		const T& top()
		{
			return _con.back();
		}
		size_t size()
		{
			return _con.size();
		}

	private:
		Container _con;
	};
}
//test.cpp
//测试代码
int main()
{
	MySTL::stack<int, vector<int>> s1;
	//MySTL::stack<int, list<int>> s1;
	s1.push(1);
	s1.push(2);
	s1.push(3);
	s1.push(4);
	s1.push(5);

	while (!s1.empty())
	{
		cout << s1.top() << " ";
		s1.pop();
	}
	cout << endl;
	return 0;
}

Container 这个模板参数的意义就在于能够让我们哦stack更加的灵活!因为无论是vector实现的栈和list实现的栈都有各自的优点!都难以互相代替!既然如此就将其写成一个模板参数!需要的时候就替换!

我们可以给这个模板参数一个缺省值默认是vector,想要的适合再进行替换!

queue的实现

#include<list>
#include<vector>
namespace MySTL
{
	template<class T,class Container = std::list<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);//尾插
		}
		void pop()
		{
			_con.pop_front();//头删
		}
		bool empty()
		{
			return _con.empty();
		}
		const T& front()//获取头元素
		{
			return _con.front();
		}
		const T& back()//获取尾元素
		{
			return _con.back();
		}
		size_t size()
		{
			return _con.size();
		}	
	private:
		Container _con;
	};
}

//test.cpp
#include "queue.h"
int main()
{
	MySTL::queue<int> s1;
	//MySTL::queue<int,list<int>> s1;
    //MySTL::queue<int,vector<int>> s1;
	s1.push(1);
	s1.push(2);
	s1.push(3);
	s1.push(4);
	s1.push(5);

	while (!s1.empty())
	{
		cout << s1.front() << " ";
		s1.pop();
	}
	cout << endl;
	return 0;
}

 MySTL::queue<int,vector<int>> s1;

这个使用vector的时候,使用pop的会报错!因为vector是不支持pop_front!

也不提倡使用vector作为队列的底层!因为这样子头删的效率会很低!是O(N)

stack和queue虽然说是容器!但是准确的说是容器适配器!——是用容器适配转换出来的!

双端队列——deque

image-20230403111808796

我们可以看到库里面的stack和queue全部的默认容器其实不是list和vector!而是deque——这是双端队列!

为什么是用deque这个容器呢?——这就不得不提到vector和list的缺点了!

vector缺点

  1. 头部,中部插入删除的效率低
  2. 扩容存在消耗
  3. 不支持头删!——pop_front ,也就是说不支持队列!

list缺点

  1. 不支持随机访问!
  2. CPU高速缓存的命中率低!

所以为了兼具list和vector的优点,于是有了双端队列这一个数据结构!

image-20230403113235228

image-20230403113405712

兼具这list和vector的所有操作!有着list和vector的优点!既可以头插头删,又可以随机访问!

#include<deque>
#include<iostream>
using namespace std;

int main()
{
	deque <int> d;
	d.push_back(1);
	d.push_back(2);
	d.push_back(3);
	d.push_back(4);

	d.push_front(10);
	d.push_front(20);

	for (size_t i = 0; i < d.size(); i++)
	{
		cout << d[i] << " ";
	}

	return 0;
}

image-20230403113822772

看起来deque十分的完美?但是真的是这样吗?

其实不是的!如果deque真的那么的完美,那么vector和list就不需要存在了!

deque的底层结构

这里我们简单的介绍一下deque的底层结构!

deque是由多个buff数组构成的!——而由一个==中控数组(指针数组)==来管理所有的buff数组

image-20230403165219806

等一个buff数组满了之后,如果对于一般的vector,就应该开始扩容,但是对于deque,不会进行扩容,而是开第二个buff数组

image-20230403165956827

第二个buff满了就开下一个,直到中控数组的也满了之后!才需要对中控数组进行扩容!

上面的插入操作都是尾插!

如果要进行头插呢?——是要挪动位置吗?不是!而是也是新开一个数组!

头插插入那个buff数组最右边开始!

这样子设计,它的CPU缓冲区命中率也可以,头插效率也不错!——虽然看上去也会有空间浪费!但是其实buff数组一般都比较小,相比vector一次性二倍扩或者1.5倍扩,造成的浪费是可以接受的!

那么这个deque的随机访问是如何实现的?

image-20230403172151456

这也是deque的第一个问题,随机访问有一定的消耗!没有vector的随机访问效率高——但是比起list的每一次都要遍历查找效率又高很多!

那么我们还有个问题,如果我们想要在14的后面进行插入一个5要怎么办?——答案是要进行挪动!

image-20230403172428170

所以这既是deque的第二个问题,对于中间的插入和删除也存在一定消耗! 没有list的中间插入删除效率高——但是又相比vector每一次的中间插入删除都要大部分挪动,效率又更高!因为只要挪动一小部分!

所以deque是一个折中的一种方案!所以这就是为什么它被使用与stack和queue的默认容器!相比vector没有扩容,相比list高速缓存命中率也不错!

中间插入删除少,头插头删,尾插尾删多,偶尔随机下标访问的时候使用deque是不错的!

但是如果要大量的进行随机访问或者进行排序还是得使用vector!

image-20230403173937505

有差不多两倍的性能差距!

如果要大量的中间插入删除还是得使用list

因为deque的模拟实现十分的复杂,我们这里就不进行模拟实现了!

如果有兴趣读者可以看《STL源码剖析》这本书去深入钻研

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/634180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TClientDataSet 模拟 EXCEL表

日常处理数据时&#xff0c;经常需要&#xff0c;从EXCEL表格中&#xff0c;批量导入数据&#xff0c;通过 XLSReadWriteII编程&#xff0c;会很快导入。 但是&#xff0c;客户提供的EXCEL表的字段&#xff0c;数据格式&#xff0c;字段的排序&#xff0c;有很大的区别。因此&a…

PostmanScript脚本功能使用详解!

目录 前言&#xff1a; 一、Pre-requestScript 二、TestScript 三、随机参数&#xff1a; 前言&#xff1a; Postman 是一个强大的 API 工具&#xff0c;可以用于构建、测试和文档化 Web API。Postman 还提供了一个名为 PostmanScript 的功能&#xff0c;它可以用于自动化…

【5】Midjourney Prompt

Prompt 是Midjourney Bot解释以生成图像的简短文本短语。 Midjourney Bot将Prompt 中的单词和短语分解成为Token的较小部分&#xff0c;可以将其与训练数据进行比较&#xff0c;然后用于生成图像。 精心制作的 Prompt可以帮助生成独特且令人兴奋的图像。 Structure 结构 基本…

Spring Boot + Vue3前后端分离实战wiki知识库系统<八>--分类管理功能开发二

接着上一次Spring Boot Vue3 前后端分离 实战 wiki 知识库系统&#xff1c;七&#xff1e;--分类管理功能开发的分类功能继续完善。 分类编辑功能优化&#xff1a; 概述&#xff1a; 现在分类编辑时的界面长这样&#xff1a; 很明显目前的父分类的展现形式不太人性&#xf…

【JVM】JVM常用指令

文章目录 1、jps2、jinfo3、jstat4、jstack5、jmap6、jhat 1、jps jps&#xff08;java process status tool&#xff09;&#xff0c; 用于查看java进程及相关信息&#xff0c;如果你想找到一个java进程的pid&#xff0c;可以使用jps命令代替linux的ps命令。 命令格式&#…

ansible常见模块应用简介

目录 command, shell, raw模块file 模块copy 模块fetch 模块lineinfile模块unarchive解包解压缩 模块user 模块yum_repository 仓库管理yum/dnf模块Service/systemd模块firewalld 模块nmcli 模块get_url 模块mount模块 注意&#xff1a;该文档需要有 Linux 基础的看 command, s…

【架构基础】高内聚低耦合

软件设计目标&#xff1a;实现需求、易于重用、易于理解、没有冗余。 Dont reinvent the wheel, just realign it. --Anthony J D’ Angelo 高内聚低耦合&#xff0c;是软件工程中判断软件设计好坏的标准。主要评判模块或类的内聚性是否高&#xff0c;耦合度是否低。目的是使…

【图像处理】基于双目视觉的物体体积测量算法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

TCP的安全和效率机制

目录 0.TCP协议格式 ​编辑 一.确认应答(安全机制) 二.超时重传(安全机制) 1.SYN丢包 2.ACK丢包 三.连接管理(安全机制) 1.三次握手建立连接 ​编辑 2.四次挥手断开连接 3.建立和断开连接 四.滑动窗口(效率机制) 五.流量控制(效率机制) 六.拥塞控制(安全机制) 七…

算法扩展第一次:收集雪花 【hash表,双指针,stl中的map】

算法详解 这道题需要新学的知识一个是双指针&#xff0c;一个是c库中的unordered_map容器 双指针 双指针原先我写过很多这方面的题&#xff0c;但是这道题我一开始是低估了它的难度&#xff0c;而且压根没有想到要用双指针&#xff0c;属于是长见识了&#xff0c;这道题的双…

Virtual box安装Ubuntu1804乱码

Virtual box安装Ubuntu1804乱码 1. 首先检查编码格式 运行以下命令打开locale配置文件&#xff1a; sudo nano /etc/default/locale2. 可能缺少字体&#xff0c;打开终端&#xff0c;先执行更新 sudo apt-get update 接着进入设置&#xff0c;搜索language&#xff0c;进入…

机器学习-搭建轻量级GPT2训练对话

在自己的机器上部署一个GPT简直太酷啦&#xff0c;因为模型数据缘故&#xff0c;所以这个机器人有时候傻傻的。。。 需要安装环境&#xff1a;python3.7 、Transformers4.2.0、pytorch1.7.0、nginx&#xff08;映射网页文件&#xff09; 我的系统&#xff1a;MAC m2 Mac默认是…

Jmeter如何安装jp@gc - Ultimate Thread Group插件(终极线程组)

首先明确一点&#xff0c;我们为什么要做压力测试&#xff1f; 压力测试是为了确保系统能够在负载高峰期和长时间运行的情况下保持高性能、稳定和可靠。同时也是软件开发生命周期中不可或缺的一环&#xff0c;帮助开发人员和系统管理员优化和调整系统&#xff0c;以提供卓越的…

2023春期末考试选择题R2-8计算最小生成树总权重详解

题目如图&#xff1a; 分析和计算&#xff1a; 题目给出一个图的邻接矩阵表示&#xff0c;要求求最小生成树的总开销。 根据Kruskal算法&#xff0c;根据邻接矩阵顶点连接情况&#xff0c;收集开销最小的边&#xff0c;直到所有顶点被收集&#xff0c;且无环路&#xff0c;即…

Debian 12 “bookworm“ 发布 - 通用操作系统

Debian 12 “bookworm” 发布 - 通用操作系统 基于 Linux kernel 6.1 LTS&#xff0c;支持 APFS 读写 请访问原文链接&#xff1a;https://sysin.org/blog/debian-12/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Debian 1…

(数组) 1365. 有多少小于当前数字的数字 ——【Leetcode每日一题】

❓1365. 有多少小于当前数字的数字 难度&#xff1a;简单 给你一个数组 nums&#xff0c;对于其中每个元素 nums[i]&#xff0c;请你统计数组中比它小的所有数字的数目。 换而言之&#xff0c;对于每个 nums[i] 你必须计算出有效的 j 的数量&#xff0c;其中 j 满足 j ! i 且…

希捷科技:具有周期性价值的全球云存储之王

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;根据Statista的数据&#xff0c;希捷科技是全球硬盘驱动器市场的领导者&#xff0c;在全球拥有约43%的市场份额。 &#xff08;2&#xff09;希捷科技的管理层近期已经宣布了一…

Node.js模块化学习笔记

Node.js模块化 模块化雨模块 将一个复杂的程序文件依据一定规则&#xff08;规范&#xff09;拆分成多个文件的过程称之为模块化。 其中拆分的每个文件就是一个模块&#xff0c;模块的内部数据是私有的&#xff0c;不过模块可以暴露内部数据以便其他模块使用 模块化项目 编…

Python课期末考试复习

简答 定义函数的规则 1、函数代码块以def关键词开头&#xff0c;后接函数标识符名称和圆括号() 2、任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。 3、函数的第一行语句可以选择性的使用文档字符串用于存放函数说明。 4、函数内容以冒号起始&#xf…

从前序与中序遍历序列构造二叉树

题目链接 从前序与中序遍历序列构造二叉树 题目描述 注意点 inorder.length preorder.lengthpreorder 和 inorder 均 无重复 元素inorder 均出现在 preorderpreorder 保证 为二叉树的前序遍历序列inorder 保证 为二叉树的中序遍历序列 解答思路 前序遍历的首个节点为根节…