【c++篇】:栈、队列、优先队列:容器世界里的秩序魔法 - stack,queue与priority_queue探秘

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨ 文章所属专栏:c++篇–CSDN博客

在这里插入图片描述

文章目录

  • 前言
  • 一.容器stack
    • 1.介绍
    • 2.成员函数
    • 3.模拟实现
    • 4.注意事项
  • 二.容器queue
    • 1.介绍
    • 2.成员函数
    • 3.模拟实现
    • 4.注意事项
  • 三.容器priority_queue
    • 1.介绍
    • 2.使用
    • 3.模拟实现
      • 基本框架
      • 成员函数
    • 4.测试

前言

在编程的世界里,数据结构是构建高效程序的基石。而在C++等编程语言中,容器则是对数据结构的一种方便的实现与封装。其中,stack(栈)queue(队列)priority_queue(优先队列)是非常重要且基本的容器类型。Stack以其独特的后进先出(LIFO)的操作模式,为解决一些特定顺序相关的问题提供了简洁的方法,例如函数调用栈。Queue遵循先进先出(FIFO)的原则,如同现实生活中的排队场景,在任务调度等场景有着广泛的应用。而priority_queue则允许按照用户定义的优先级来取出元素,给具有不同重要性元素的操作提供了高效的实现。本博客旨在深入探讨这三种容器,包括它们的特性、操作方式以及各自适用的应用场景等,希望能够帮助读者更好地理解和运用这些在编程中至关重要的工具。

一.容器stack

1.介绍

在之前的数据结构学习中我们知道Stack(栈)是一种特殊的线性数据结构,其特点在于元素的插入和删除操作都只能在容器的一端进行,这一端通常被称为栈顶。Stack遵循后进先出LIFO, Last In First Out的原则,即最后插入的元素会是第一个被删除的元素。

在C++中,Stack是作为容器适配器实现的,这意味着它封装了某个具体的容器类(如vectordequelist)作为其底层存储结构,并提供了一套特定的成员函数来访问这些元素。如果没有为Stack指定特定的底层容器,默认情况下,Stack会使用deque作为其底层容器,因为deque提供了在两端高效插入和删除元素的能力,非常适合Stack的需求。

2.成员函数

Stack提供的主要成员函数包括:

  • push(x)函数

    • 将元素x压入栈顶。
  • pop()

    • 弹出栈顶元素,即删除栈顶元素。注意:这个函数没有返回值,只是简单地移除了栈顶元素。
  • top()

    • 返回栈顶元素的引用,但不删除它。这允许我们访问栈顶元素的值,注意:如果不小心修改了引用的值,将会改变栈顶元素的值。
  • empty()

    • 检查栈是否为空。如果栈为空,则返回true;否则返回false。
  • size()

    • 返回栈中元素的个数。

3.模拟实现

以下是一个容器Stack的简单模拟实现:

  • 代码实现:
#include<iostream>
#include<vector>
#include<list>
using namespace std;

namespace Mystack{
    template<class T,class container=vector<T>>
    class stack{
    public:
        //构造函数
        stack()
        {}

        void push(const T& x){
            //入栈调用尾插函数
            _con.push_back(x);
        }
        void pop(){
            //出栈调用尾删函数
            _con.pop_back();
        }
        T& top(){
            //取栈顶元素返回最后一个元素即可
            return _con[_con.size()-1];
        }
        const T& top()const {
            return _con[_con.size()-1];
        }
        size_t size()const {
            //获取大小调用size()函数
            return _con.size();
        }
        bool empty()const {
            //判断为空调用empty()函数
            return _con.empty();
        }
    private:
        container _con;

    };
}
  • 实现原理:

    • 模版参数T表示存储的数据类型,container表示适配器的类型,默认为vector<T>
    • 成员变量_con是适配器的实例化对象
    • 构造函数为空即可,成员变量会调用对应容器的构造函数完成初始化
    • top()函数分为普通对象使用的类型和const对象使用的类型,返回是引用返回
  • 测试用例:

    void test1(){
        Mystack::stack<string> st;
        st.push("ab");
        st.push("cd");
        st.push("ef");
        st.push("gh");
        while(!st.empty()){
            cout<<st.top()<<" ";
            st.pop();
        }
        cout<<endl;
    }
    

    在这里插入图片描述

4.注意事项

  1. Stack不支持随机访问,即不能通过下标访问元素。
  2. Stack内的元素是无法直接遍历的,但可以通过while循环和pop()/top()的组合来遍历(但这种方法会改变Stack的状态,因为每读取一个元素就需要弹出这个元素)。
  3. 在使用pop()函数之前,需要确保栈不为空,否则会导致未定义行为。

二.容器queue

1.介绍

Queue(队列)是一种先进先出FIFO, First In First Out的数据结构,它有两个主要操作:入队(enqueue)和出队(dequeue)。入队操作将元素添加到队列的尾部,而出队操作则移除队列头部的元素。Queue在多种场景中都有广泛的应用,如任务调度、消息传递、广度优先搜索等。

在C++中,Queue是作为容器适配器(Container Adapter)实现的,它依赖于某个具体的容器类(如dequelist)作为其底层存储结构。默认情况下,C++标准库中的Queue使用deque作为其底层容器,但也可以通过模板参数指定其他支持双端操作的容器,如list

2.成员函数

Queue提供的主要成员函数包括:

  • push(x)

    • 将元素x添加到队列的尾部。
  • pop()

    • 移除队列头部的元素。注意,这个函数没有返回值,只是简单地移除了队列头部的元素。
  • front()

    • 返回队列头部元素的引用,但不删除它。这允许我们访问队列头部元素的值。
  • back()

    • 返回队列尾部元素的引用,但不删除它。这允许我们访问队列尾部元素的值。
  • empty()

    • 检查队列是否为空。如果队列为空,则返回true;否则返回false。
  • size()

    • 返回队列中元素的个数。

3.模拟实现

以下是一个容器Queue的简单模拟实现(注意,c++标准库中使用的是deque做默认适配器,这里为了方便直接使用list做默认适配器:

  • 代码实现:
#include<iostream>
#include<vector>
#include<list>
using namespace std;

namespace Myqueue{
    template<class T,class container=list<T>>
    class queue{
    public:
        //构造函数
        queue()
        {}
        
        //入队调用尾插函数
        void push(const T& x){
            _con.push_back(x);
        }
        //出队调用头删函数
        void pop(){
            _con.pop_front();
        }
        //取队头元素返回迭代器begin()位置的元素
        T& front(){
            return *(_con.begin());
        }
        const T& front()const {
            return *(_con.begin());
        }
        //取队尾元素返回迭代器end()位置的元素
        T& back(){
            return *(_con.end()--);
        }
        const T& back()const {
            return *(_con.end()--);
        }
        //获取队长调用size()函数
        size_t size()const {
            return _con.size();
        }
        //判断为空调用empty()函数
        bool empty()const {
            return _con.empty();
        }
    private:
        container _con;
    };
}
  • 实现原理:

    • 模版参数T表示存储的数据类型,container表示适配器的类型,默认为list<T>
    • 成员变量_con是适配器的实例化对象
    • 构造函数为空即可,成员变量会调用对应容器的构造函数完成初始化
    • back()front函数分为普通对象使用的类型和const对象使用的类型,返回是引用返回
  • 测试用例:

void test2(){
    Myqueue::queue<string> q;
    q.push("ab");
    q.push("cd");
    q.push("ef");
    q.push("gh");
    while(!q.empty()){
        cout<<q.front()<<" ";
        q.pop();
    }
    cout<<endl;
}

在这里插入图片描述

4.注意事项

  1. Queue不支持随机访问,即不能通过下标访问元素。
  2. 在使用pop()函数之前,需要确保队列不为空,否则会导致未定义行为。同样地,在使用front()back()函数之前,也需要确保队列不为空。
  3. Queue的底层容器默认是deque,但也可以通过模板参数指定为其他支持双端操作的容器,如list。然而,由于vector只支持在尾部进行高效的插入和删除操作,因此它不能作为Queue的底层容器。

通过本文的介绍,相信你已经对C++中的Queue容器有了基本的了解,并掌握了其使用方法。希望这些信息能对你有所帮助!

三.容器priority_queue

容器priority_queue(优先级队列)是C++标准库中的一个容器适配器,它根据严格的弱排序标准为元素提供排序,使得队列的第一个元素总是当前元素中优先级最高的(默认是大堆,即最大元素位于堆顶)。

1.介绍

  1. 基本概念

    • 优先队列类似于堆,可以随时插入元素,并且只能检索最大堆元素(即队首元素)。
    • 优先队列被实现为容器适配器,将特定的容器类封装作为其底层容器。
  2. 底层容器

    • 底层容器可以是任何标准容器类模板,如vectordeque,这些容器应该可以通过随机访问迭代器访问。
    • 默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector作为底层容器。
  3. 比较函数

    • 优先队列使用比较函数来确定元素的优先级。默认情况下,使用std::less作为比较函数,创建最大堆。
    • 若要使用最小堆,可以指定std::greater作为比较函数。

2.使用

  1. 包含头文件
    在使用priority_queue之前,需要包含<queue>头文件。

  2. 定义优先队列

    priority_queue<int> pq; // 默认最大堆
    priority_queue<int, vector<int>, greater<int>> pq_min; // 最小堆
    
  3. 成员函数

    • push(x):在优先队列中插入元素x
    • pop():删除优先队列中最大(或最小)元素。
    • top():返回优先队列中最大(或最小)元素。
    • empty():检测优先队列是否为空。
    • size():返回优先队列中有效元素的个数。

3.模拟实现

基本框架

  • 代码实现:

    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    namespace Mypriority_queue{
        //仿函数/函数对象
        //这个类可以像函数一样使用
        //用来控制建大堆
        template<class T>
        class less{
        public:
            bool operator()(const T& x,const T& y){
                return x<y;
            }
        };
        //用来控制建小堆
        template<class T>
        class greater{
        public:
            bool operator()(const T& x,const T& y){
                return x>y;
            }
        };
        
        //类模版
        template<class T,class container=vector<T>,class comapre=less<T>>
        class priority_queue{
        private:
            //向上调整建堆
            //向下调整建堆
            
        public:
            //....其他成员函数
            
        private:
            //成员变量
            container _con;
            
        };
    }
    
  • 实现原理:

    • 定义两个类,这两个可以像函数一样使用,也叫做仿函数,用来控制建大堆还是建小堆
    • priority_queue类中,模版参数T表示存储的数据类型,模版参数container表示适配器,默认适配器为vector<int>,模版参数comapre表示用来控制建堆

成员函数

  • 向下调整建堆代码实现:

    void AdjustDown(size_t parents){
        comapre com;
        //向下调整,由父节点找子节点,父节点乘2再加1
        size_t child=parents*2+1;
        if(child+1<_con.size()&&com(_con[child],_con[child+1])){
        child++;
        }
        while(child<_con.size()){
            if(com(_con[parents],_con[child])){
                swap(_con[parents],_con[child]);
                parents=child;
                child=parents*2+1;
            }
            else{
                break;
            }
        }
    }
    
  • 实现原理:

    • 由传过来的父节点找子节点,子节点是父节点的下标乘以2再加1
    • 创建com对象,这个对象可以像使用函数一样用来比较大小
  • 向下调整建堆代码实现:

    void AdjustUp(size_t child){
        comapre com;
        //向上调整,由子节点找父节点,子节点先-1再除2
         size_t parents=(child-1)/2;
         while(child>0){
             if(com(_con[parents],_con[child])){
                 swap(_con[parents],_con[child]);
                 child=parents;
                 parents=(child-1)/2;
              }
              else{
                 break;
              }
         }
    }
    
  • 实现原理:

    • 由传过来的子节点找父节点,父节点下标是子节点下标先减1再除以2
    • 创建com对象,这个对象可以像使用函数一样用来比较大小
  • 构造函数代码实现:

    template<class InputIterator>
    priority_queue(InputIterator first,InputIterator last){
        while(first!=last){
            _con.push_back(*first);
            first++;
        }
        //向下调整建堆
        //注意,for循环中,i最好不要用size_t,如果出现i小于0时,会死循环
        for(int i=(_con.size()-1-1)/2;i>=0;i--){
            AdjustDown(i);
        }
    }
    
  • 实现原理:

    • 构造函数是模版函数,将区间中的数据依次插入到优先队列中
    • 插入完数据后循环调用向下调整建堆
  • 其他成员函数代码实现:

    void pop(){
        swap(_con[0],_con[_con.size()k-1]);
        _con.pop_back();
        AdjustDown(0);
    }
    
    void push(const T& x){
        _con.push_back(x);
        AdjustUp(_con.size()-1);
    }
    
    T& top(){
        return _con[0];
    }
    
    const T& top()const {
        return _con[0];
    }
    
    size_t size()const {
        return _con.size();
    }
    
    bool empty()const {
        return _con.empty();
    }
    
  • 实现原理:

    • pop()函数先交换第一个和最后一个元素,在删除最后一个元素相当于删除堆顶的元素,再调用向下调整
    • push()函数将需要插入的数据插入到最后位置,再调用向上调整
    • top()函数相当于获取堆顶也就是第一个元素
    • 获取大小调用对应的size()函数
    • 判断为空调用对应的empty()函数

4.测试

  • 测试代码:

    void test1(){
        vector<int> v={3,2,6,1,8,7,0};
        Mypriority_queue::priority_queue<int> q(v.begin(),v.end());
        q.push(4);
        q.push(5);
        while(!q.empty()){
            cout<<q.top()<<" ";
            q.pop();
        }
        cout<<endl;
    }
    
    int main(){
        test1();
        return 0;
    }
    
  • 测试结果:

    在这里插入图片描述
    以上就是关于容器stack,queue,priority_queue的基本使用和模拟实现的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
    在这里插入图片描述

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

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

相关文章

Java基础——循环switch大数值更改器访问器深浅拷贝

目录 1.循环 2.switch多分支选择结构 3.大数值 4.浅拷贝&深拷贝 5.Arrays.sort排序 6.面向对象 7.更改器&访问器 1.循环 for-each循环 在 Java 中&#xff0c;for-each 循环&#xff08;也称为增强型 for 循环&#xff09;是一种用于遍历数组或集合&#xff08…

引入 axios,根据 api 文档生成调用接口

起步 | Axios Docs 安装 axios npm install axios 生成 api 调用接口【可选】 https://github.com/ferdikoomen/openapi-typescript-codegen 安装 npm install openapi-typescript-codegen --save-dev 然后执行生成代码 # http://localhost:8805/api/user/v3/api-docs&a…

wsl2+Ubuntu安装图形化界面(VNC方式)

本章教程,记录在wsl2+Ubuntu操作系统中安装可视化界面步骤。 一、安装桌面环境 尽量以root用户进行安装,避免因权限不足导致部分依赖无法安装。 sudo apt update sudo apt upgrade -y sudo apt install ubuntu-desktop由于可视化桌面程序安装包比较大,网速慢的小伙伴,需要耐…

Linux应用——线程池

1. 线程池要求 我们创建线程池的目的本质上是用空间换取时间&#xff0c;而我们选择于 C 的类内包装原生线程库的形式来创建&#xff0c;其具体实行逻辑如图 可以看到&#xff0c;整个线程池其实就是一个大型的 CP 模型&#xff0c;接下来我们来完成它 2. 整体模板 #pragma …

学习日记_241110_局部线性嵌入(Locally Linear Embedding, LLE)

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…

【我的 Anti-AV 学习手札】DLL注入

无敌舍友s神免杀学了一个阶段&#xff0c;达者为师&#xff0c;向s师傅学习&#xff01;&#xff01; ps&#xff1a;我的基础实在薄弱&#xff0c;WIN编程甚至都没做过&#xff0c;所以笔记翔实些 一、注入思路 1.在进程中开辟一段空间 2.存入dll绝对路径地址的字符串 3.使用…

【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)

关键词&#xff1a;一多、响应式、媒体查询、栅格布局、断点、UI 随着设备形态的逐渐增多&#xff0c;应用界面适配也面临着很大问题&#xff0c;在以往的安卓应用开发过程中&#xff0c;往往需要重新开发一套适用于大屏展示的应用&#xff0c;耗时又耗力&#xff0c;而鸿蒙提供…

linux 安装 mongodb

选择MongoDB版本 https://www.mongodb.com/try/download/community-kubernetes-operator 我的系统是centos7.9 这里只能最高只能选择mongo7 复制下载链接&#xff1a;https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.15.tgz 获取安装教程&#xff1a; h…

《深入浅出Apache Spark》系列②:Spark SQL原理精髓全解析

导读&#xff1a;SQL 诞生于 20 世纪 70 年代&#xff0c;至今已有半个世纪。SQL 语言具有语法简单&#xff0c;低学习门槛等特点&#xff0c;诞生之后迅速普及与流行开来。由于 SQL 具有易学易用的特点&#xff0c;使得开发人员容易掌握&#xff0c;企业若能在其计算机软件中支…

PostgreSQL pg-xact(clog)目录文件缺失处理

一、 背景 前些天晚上突然收到业务反馈&#xff0c;查询DB中的一个表报错 Could not open file "pg-xact/005E": No such file or directory. 两眼一黑难道是文件损坏了...登录查看DB日志&#xff0c;还好没有其他报错&#xff0c;业务也反馈只有这一个表在从库查询报…

Cursor的chat与composer的使用体验分享

经过一段时间的试用&#xff0c;下面对 Composer 与 Chat 的使用差别进行总结&#xff1a; 一、长文本及程序文件处理方面 Composer 在处理长文本时表现较为稳定&#xff0c;可以对长文进行更改而不会出现内容丢失的情况。而 Chat 在更改长的程序文件时&#xff0c;有时会删除…

MATLAB课程:AI工具辅助编程——MATLAB+LLMs

给出一些可能有用的方法辅助大家写代码。 方法一&#xff1a;MATLAB软件LLM (不太懂配置的同学们为了省事可以主要用这个方法) 方法一特别针对本门MATLAB教学课程&#xff0c;给出一种辅助ai工具的操作指南。MATLAB中可以安装MatGPT插件&#xff0c;该插件通过调用ChatGPT的API…

2.索引:SQL 性能分析详解

SQL性能分析是数据库优化中重要的一环。通过分析SQL的执行频率、慢查询日志、PROFILE工具以及EXPLAIN命令&#xff0c;能够帮助我们识别出数据库性能的瓶颈&#xff0c;并做出有效的优化措施。以下将详细讲解这几种常见的SQL性能分析工具和方法。 一、SQL 执行频率 SQL执行频率…

使用Go语言编写一个简单的NTP服务器

NTP服务介绍 NTP服务器【Network Time Protocol&#xff08;NTP&#xff09;】是用来使计算机时间同步化的一种协议。 应用场景说明 为了确保封闭局域网内多个服务器的时间同步&#xff0c;我们计划部署一个网络时间同步服务器&#xff08;NTP服务器&#xff09;。这一角色将…

深度学习经典模型之VGGNet

1 VGGNet 1.1 模型介绍 ​ VGGNet是由牛津大学视觉几何小组&#xff08;Visual Geometry Group, VGG&#xff09;提出的一种深层卷积网络结构&#xff0c;他们以7.32%的错误率赢得了2014年ILSVRC分类任务的亚军&#xff08;冠军由GoogLeNet以6.65%的错误率夺得&#xff09;和…

Android的BroadcastReceiver

1.基本概念&#xff1a;BroadCast用于进程间或者线程间通信 本质上是用Binder方法&#xff0c;以AMS为订阅中心&#xff0c;完成注册&#xff0c;发布&#xff0c;监听的操作。 2.简单实现的例子 package com.android.car.myapplication;import android.content.BroadcastRe…

分布式数据库中间件mycat

MyCat MyCat是一个开源的分布式数据库系统&#xff0c;它实现了MySQL协议&#xff0c;可以作为数据库代理使用。 MyCat(中间件)的核心功能是分库分表&#xff0c;即将一个大表水平分割为多个小表&#xff0c;存储在后端的MySQL服务器或其他数据库中。 它不仅支持MySQL&#xff…

Java多线程编程(四)- 阻塞队列,生产者消费者模型,线程池

目录&#xff1a; 一.阻塞队列 二.线程池 一.阻塞队列 1.阻塞队列是⼀种特殊的队列. 也遵守 "先进先出" 的原则 阻塞队列能是⼀种线程安全的数据结构, 并且具有以下特性&#xff1a; 1.1.当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素 1.…

深度剖析JUC中LongAdder类源码

文章目录 1.诞生背景2.LongAdder核心思想3.底层实现&#xff1a;4.额外补充 1.诞生背景 LongAdder是JDK8新增的一个原子操作类&#xff0c;和AtomicLong扮演者同样的角色&#xff0c;由于采用AtomicLong 保证多线程数据同步&#xff0c;高并发场景下会导致大量线程同时竞争更新…

大数据面试题--kafka夺命连环问

1、kafka消息发送的流程&#xff1f; 在消息发送过程中涉及到两个线程&#xff1a;一个是 main 线程和一个 sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给双端队列&#xff0c;sender 线程不断从双端队列 RecordAccumulator 中拉取…