【C++】容器篇(四)—— queue的基本介绍以及模拟实现

前言:

在上期博文中我带大家对stack进行深入的学习,本期我将带领学习的是关于 queue的基本知识,并且还将给大家介绍并实现  priority_queue。接下来,让我们正式本期的内容。


目录

(一)queue的基本介绍

(二)基本使用

(三)模拟实现

(四)priority_queue的基本介绍

(五)基本使用

(六)priority_queue的模拟实现

(六)deque的简单介绍(了解)

1、deque的原理介绍

2、deque的缺陷

(七)deque作为stack和queue的底层默认容器

1、分析

2、deque的优势

3、原因

总结


(一)queue的基本介绍

  • 文档链接:queue的文档介绍

 💨  queue是一个容器适配器,它是基于deque或者list实现的。queue的特点是先进先出(FIFO)

底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

  1. empty:检测队列是否为空
  2. size:返回队列中有效元素的个数
  3. front:返回队头元素的引用
  4. back:返回队尾元素的引用
  5. push_back:在队列尾部入队列
  6. pop_front:在队列头部出队列

标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque


 

(二)基本使用

  • queue的主要的接口函数大概就是以下这几个,我们也将围绕这几个接口来模拟实现。


(三)模拟实现

  • 代码如下:
#pragma once
namespace zp
{
	template<class T, class Container = deque<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			return _qq.push_back(x);
		}

		void pop()
		{
			if (!_qq.empty())
			{
				return _qq.pop_front();
			}
		}

		const T& front()
		{
			return _qq.front();
		}

		const T& back()
		{
			return _qq.back();
		}

		bool empty()
		{
			return _qq.empty();
		}

		size_t size()
		{
			return _qq.size();
		}

	private:
		Container _qq;
	};

	void test_queue_1()
	{
		queue<int> q;
		//queue<int, list<int>>q;
		//queue<int, vector<int>>q;
		q.push(1);
		q.push(2);
		q.push(3);
		q.push(4);
		q.push(5);


		//队首元素
		if ((!q.empty()))
		{
			cout << q.front() << " "<<endl;
		}
	
		q.pop();
		cout << q.front() << endl;

		// 检查队列是否为空
		if (q.empty())
		{
			cout << "Stack is empty" << endl;
		}
		else
		{
			cout << "queue is not empty" << endl;
		}

		// 获取队列的大小
		cout << "queue size is " << q.size() << endl;

		//输出队列元素
		while (!q.empty())
		{
			cout << q.front() << " ";
			q.pop();
		}
		cout << endl;
	}
}

 

(四)priority_queue的基本介绍

  • 文档链接:priority_queue的文档介绍

priority_queue是一个容器适配器,它是基于vector实现的,默认情况下是按照元素大小排序的(大根堆)。可以通过指定比较函数改变排序方式。

底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭 代器访问,并支持以下操作:

  1. push():元素入队
  2. pop():元素出队
  3. top():返回堆顶元素
  4. empty():判断堆是否为空
  5. size():返回堆中元素数量

 💨  大家如果想了解更多,可以借助文档仔细琢磨!!!


(五)基本使用

  • 优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成 堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。

基本使用跟上面的queue是大同小异的,我们直接模拟实现即可。

⚔️  注意: 默认情况下priority_queue是大堆。⚔️ 


(六)priority_queue的模拟实现

  • 代码如下:
#pragma once
namespace zp
{
    template<class T, class Compare = less<T>>
    class priority_queue 
    {
    public:
        void push(const T& val) 
        {
            _qq.push_back(val);
            push_heap(_qq.begin(), _qq.end(), _cmp);
        }

        void pop() 
        {
           pop_heap(_qq.begin(), _qq.end(), _cmp);
           _qq.pop_back();
        }

        const T& top() const 
        {
            return _qq.front();
        }

        bool empty() const 
        {
            return _qq.empty();
        }

        size_t size() const 
        {
            return _qq.size();
        }

    private:
        vector<T> _qq;
        Compare _cmp;
    };

    void test_priority_queue()
    {
        priority_queue<int> pq;
        pq.push(1);
        pq.push(3);
        pq.push(2);
        pq.push(5);
        pq.push(4);

        while (!pq.empty())
        {
            cout << pq.top() << " ";
            pq.pop();
        }
        cout << endl;
    }
}

(六)deque的简单介绍(了解)

  • 文档链接:deque文档介绍

1、deque的原理介绍

deque(双端队列)

  • 是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1);
  • 与vector比较,头插效率高,不需要搬移元素;
  • 与list比较,空间利用率比较高。

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

 

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:

 


那deque是如何借助其迭代器维护其假想连续的结构呢?

  1. deque是一个由多个固定大小的缓冲区构成的序列容器,每个缓冲区都被当作一个元素来处理。为了维护deque的假想连续结构,deque的迭代器实际上是一种复合迭代器,它将多个子迭代器封装在一起,以提供对整个deque的访问。
  2. 具体来说,deque的迭代器实际上就是一个指向数据缓冲区的指针数组,每个指针指向一个单独的存储块,而这些存储块构成了deque的数据缓冲区。

 

  1. deque会通过维护多个内部的缓冲区来实现假想连续的结构,并且迭代器也会对这些内部缓冲区进行适当划分和处理;
  2. 在deque内部,每个缓冲区被称作一个节点node,并在其内部维护了一个完整的数据块;
  3. 当deque在两端插入/删除元素时,它会动态重新分配节点,使得每个节点上的数据块都可以被利用,从而保证数据块是假想连续的。同时,迭代器会利用这些节点间的相对偏移量来实现对deque的高效遍历。

下面是一个简单的示例程序,演示了如何使用deque迭代器来访问deque中的元素:

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

int main() 
{
   deque<int> d = { 0,1, 2, 3, 4 };
   deque<int>::iterator it = d.begin();

    // 使用迭代器访问deque
    for (; it != d.end(); ++it) {
        cout << *it << " ";
    }
   cout <<endl;

    // 在头部和尾部插入元素
    d.push_front(6);
    d.push_back(5);

    // 再次使用迭代器访问deque
    for (it = d.begin(); it != d.end(); ++it) {
        cout << *it << " ";
    }
   cout << endl;

    return 0;
}

解释说明:

  1. 在上述代码中,我们使用deque容器和迭代器来存储和访问一系列整数;
  2. 首先,我们定义了一个deque对象d,并使用begin()函数获取其头部的迭代器;
  3. 然后,我们使用迭代器遍历deque中的元素,并输出它们的值;
  4. 接下来,我们调用push_front()函数和push_back()函数在deque的头部和尾部插入两个新元素;
  5. 最后,我们再次使用迭代器遍历deque中的元素,并输出它们的值;

 💨 可以看到,即使我们在deque中插入了新元素,迭代器仍然可以正确地指向各个元素,这得益于deque迭代器的复合结构和灵活的移动方式。

输出结果如下:


 

2、deque的缺陷

与vector比较,deque的优势是:

  • 头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是比vector高的。

与list比较,deque的优势是:

  • 其底层是连续空间,空间利用率比较高,不需要存储额外字段。

deque有一个致命缺陷:

  • 不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历;
  • 因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作 为stack和queue的底层数据结构。

(七)deque作为stack和queue的底层默认容器

1、分析

 💨 stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;

 💨 queue是先进先出的特殊线性数据结构,只要具有 push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。

2、deque的优势

  1. deque提供了在两端进行快速插入和删除的操作,并且支持随机访问。这使得它可以高效地实现栈和队列的基本操作,例如压入元素、弹出元素、获取栈顶或队头元素等等。
  2. 其次,deque的内存分配策略相对于vector更加灵活。vector的内存分配策略是连续的,在需要重新分配内存时,所有的元素都需要被复制到新的内存空间中;而deque的内存分配策略是分块的,每个元素所占用的内存空间是独立的,可以在不影响整个容器的情况下单独分配和回收。

 💨 这样,当需要重新分配内存时,只需要重新分配一部分内存空间,而不需要把所有的元素都复制到新的内存空间中。这使得deque相对于vector具有更好的动态扩展性能和更低的内存开销。

3、原因

STL中对stack和 queue默认选择deque作为其底层容器,主要是因为:

  • 1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
  • 2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。

因此综上所述,由于deque提供了高效的双端操作和灵活的内存分配策略,使得它成为了实现stack和queue的底层默认容器的理想选择,结合了deque的优点,而完美的避开了其缺陷。


总结

到此,本文的内容便到此结束了,接下来我们简单的回顾一下本文都讲了什么吧!!!

  • 1、首先,我们介绍有关队列的有关知识,知道了其FIFO的特性,并手动实现了一个queue;
  • 2、其次,我们认识了priority_queue,跟学习queue一样,我们知道了在默认情况下队列中的元素是按照大根堆的序列排的;
  • 3、紧接着,我带领大家简单的认识一下关于deque,了解了相关概念以及特性;
  • 4、最后,我们整理了deque作为stack和queue的底层默认容器的具体原因。

以上便是本文的全部内容,希望对大家有所帮助,感谢各位的支持与观看!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Eclipse教程 Ⅵ

今天分享Eclipse Java 构建路径、Eclipse 运行配置(Run Configuration)和Eclipse 运行程序 Eclipse Java 构建路径 设置 Java 构建路径 Java构建路径用于在编译Java项目时找到依赖的类&#xff0c;包括以下几项&#xff1a; 源码包项目相关的 jar 包及类文件项目引用的的类…

83.响应式设计原则

什么是响应式设计&#xff1f; ● 使网页根据任何可能的屏幕尺寸&#xff08;窗口或视口尺寸&#xff09;调整其布局和视觉风格的设计技术。 ● 在实践中&#xff0c;这意味着响应式设计使网站可以在所有设备上使用&#xff0c;如台式电脑、平板电脑和手机。 ● 这是一套做法&…

LearnOpenGL-高级OpenGL-9.几何着色器

本人初学者&#xff0c;文中定有代码、术语等错误&#xff0c;欢迎指正 文章目录 几何着色器使用几何着色器造几个房子爆破物体法向量可视化 几何着色器 简介 在顶点和片段着色器之间有一个可选的几何着色器几何着色器的输入是一个图元&#xff08;如点或三角形&#xff09;的一…

Spark入门介绍

目录 一、Spark框架概述 1、Spark简介 2、发展 二、Spark功能及特点 1、定义

K8s进阶7——Sysdig、Falco、审计日志

文章目录 一、分析容器系统调用&#xff1a;Sysdig1.1. 安装1.2 常用参数1.3 采集分析1.4 示例1.4.1 查看某进程系统调用事件1.4.2 查看建立TCP连接事件1.4.3 查看某目录下打开的文件描述符1.4.4 查看容器的系统调用 1.5 Chisels工具1.5.1 网络类1.5.2 硬盘类1.5.3 cpu类1.5.4 …

奇舞周刊第493期:Hook 革命!浅谈 React 新 Hook 的未来与思想

关注前端生态发展&#xff0c;了解行业动向。 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ Hook 革命&#xff01;浅谈 React 新 Hook 的未来与思想 作者阳羡曾写文章对 React 新 Hook use 的设计理念和限制进行了深入分析&#xff0c;并提供了一个可能的实现来帮助读者…

如何在 Alpine Linux 上启用或禁用防火墙?

防火墙是计算机网络安全的重要组成部分&#xff0c;它用于保护计算机和网络免受未经授权的访问和恶意攻击。Alpine Linux 是一种轻量级的 Linux 发行版&#xff0c;常用于构建容器化应用和嵌入式系统。本文将详细介绍如何在 Alpine Linux 上启用或禁用防火墙。步骤 1&#xff1…

Seata-Server安装

1.去哪下 发布说明: https://github.com/seata/seata/releases 2.怎么玩 本地**Transactional**全局**GlobalTransactional** 我们只需要使用一个 GlobalTransactional 注解在业务方法上: 3.Seata-Server安装 官网地址 3.1.版本0.9.0 seata-server-0.9.0.zip解压到指定目…

Linux——进程轮换

目录 一.进程切换 1.定义&#xff1a; 2.硬件上下文&#xff1a; 总结&#xff1a; 一.进程切换 1.定义&#xff1a; 进程切换(process switch),作为抢占式多任务的一个重要功能&#xff0c;其实质就是OS内核挂起正在运行的进程A,然后将先前被挂起的另一个进程B恢复运行。 2.硬…

什么是Redission可重入锁,其实现原理是什么?

一、概述 Redission是一个可重入锁&#xff0c;它可以在分布式系统中用于实现互斥锁。这种锁可以允许多个线程同时获取锁&#xff0c;但在任何给定时间只有一个线程可以执行受保护的代码块。 Redission锁提供了一种简单的方法来保证在分布式系统中的互斥性&#xff0c;同时支…

K8s in Action 阅读笔记——【5】Services: enabling clients to discover and talk to pods

K8s in Action 阅读笔记——【5】Services: enabling clients to discover and talk to pods 你已了解Pod以及如何通过ReplicaSets等资源部署它们以确保持续运行。虽然某些Pod可以独立完成工作&#xff0c;但现今许多应用程序需要响应外部请求。例如&#xff0c;在微服务的情况…

day42_jsp

今日内容 零、 复习昨日 一、JSP 二、EL 三、JSTL 四、MVC 零、 复习昨日 一、JSP 1.0 引言 现有问题 在之前学习Servlet时&#xff0c;服务端通过Servlet响应客户端页面&#xff0c;有什么不足之处&#xff1f; 开发方式麻烦&#xff1a;继承父类、覆盖方法、配置Web.xml或注…

【Proteus仿真】51单片机+步进电机驱动

【Proteus仿真】51单片机步进电机驱动 &#x1f516;Proteus仿真基础实验-步进电机驱动&#x1f33f;Proteus8.12平台 &#x1f4cb;步进电机简介 步进电机是一种将电脉冲转换为角位移的开环控制元步进电机。一般地&#xff0c;当步进驱动器接收到脉冲信号时&#xff0c;它将根…

【工作流】Activiti工作流简介以及Spring Boot 集成 Activiti7

文章目录 前言一、activiti介绍二、工作流引擎三、BPMN四、数据库五、Spring Boot 集成 Activiti7安装插件引入依赖配置文件 总结 前言 什么是工作流&#xff1f; 工作流指通过计算机对业务流程进行自动化管理&#xff0c;实现多个参与者按照预定义的流程去自动执行业务流程。 …

Linux :: vim 编辑器:详解:文本复制/粘贴/剪切/删除 与 撤销普通操作及撤销撤销操作

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 学习集&#xff1a; C 入门到入土&#xff01;&#xff01;&#xff01;学习合集Linux 从命令到网络再到内核&#xff01;学习合集 前文&#x…

【哈希】位图/布隆过滤器

位图 前言 在实现位图结构之前我们先看一个问题&#xff1a; 给出40亿个不重复的无符号整型&#xff0c;并且是无序的。然后给一个无符号整数&#xff0c;怎样快速判断这个数是否在40亿个数之中。 方法一&#xff1a;对40亿个数据进行遍历。我们会发现&#xff0c;时间复杂度…

使用kotlin用回溯法解决电话号码的字母组合问题

17. 电话号码的字母组合 难度中等 2474 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#…

C++const函数的运用:深度解析const函数的魅力

C 深度解析const函数的魅力 1. C const函数的基本概念&#xff08;Basic Concepts of const Functions in C&#xff09;1.1 const函数的定义与特性&#xff08;Definition and Characteristics of const Functions&#xff09;1.2 const函数的使用场景&#xff08;Usage Scena…

Spring(四)基于xml的自动装配

自动装配&#xff1a;根据指定的策略&#xff0c;在IOC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值。 首先我们来熟悉三层架构的创建过程&#xff1a; 三层架构为controller层&#xff0c;service层&#xff0c;dao层。 在service层里面创建ser…

php内置类小结

文章目录 php内置类小结Error、Exception进行xss、绕过hash比较Error类Exception类使用Error、Exception内置类绕过md5、sha1等哈希比较Error类详解Exception类详解例题&#xff1a;[2020 极客大挑战]Greatphp 使用DirectaryIterator、Filesystemlterator、Globlterator内置类读…