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

news2024/11/25 1:23:54

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉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的基本使用和模拟实现的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
    在这里插入图片描述

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

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

相关文章

实现uniapp-微信小程序 搜索框+上拉加载+下拉刷新

pages.json 中的配置 { "path": "pages/message", "style": { "navigationBarTitleText": "消息", "enablePullDownRefresh": true, "onReachBottomDistance": 50 } }, <template><view class…

无人机培训机型有哪些?CAAC考证选3类还是4类

无人机培训是一个涵盖多个方面的综合性过程&#xff0c;旨在培养具备无人机操作技能和相关知识的人才。 无人机培训机型 无人机培训通常涵盖多种机型&#xff0c;以满足不同领域和应用场景的需求。常见的无人机培训机型包括&#xff1a; 1. 多旋翼无人机&#xff1a;也称为多…

95.【C语言】数据结构之双向链表的头插,头删,查找,中间插入,中间删除和销毁函数

目录 1.双向链表的头插 方法一 方法二 2.双向链表的头删 3.双向链表的销毁 4.双向链表的某个节点的数据查找 5.双向链表的中间插入 5.双向链表的中间删除 6.对比顺序表和链表 承接94.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删文章 1.双向链表的头插 方法…

[极客大挑战 2019]PHP 1

[极客大挑战 2019]PHP 1 审题 猜测备份在www.zip中&#xff0c;输入下载文件。 知识点 反序列化 解题 查看代码 看到index.php中包含了class.php,直接看class.php中的代码 查看条件 当usernameadmin&#xff0c;password100时输出flag 构造反序列化 输入select中&#…

C++面试基础知识:排序算法 C++实现

上周实习面试&#xff0c;手撕代码快排没写出来&#xff0c;非常丢人&#xff0c;把面试官都给逗笑了。 基础不牢&#xff0c;地动山摇&#xff0c;基础的算法还是要牢记于心的。 插入排序 分为有序区和无序区&#xff0c;每次从无序区中选出一个&#xff0c;放到有序区域中。…

yarn报错`warning ..\..\package.json: No license field`:已解决

出现这个报错有两个原因 1、项目中没有配置许可证 在项目根目录package.json添加 {"name": "next-starter","version": "1.0.0",# 添加这一行"license": "MIT", }或者配置私有防止发布到外部仓库 {"priv…

批量缓存模版

批量缓存模版 缓存通常有两种使用方式&#xff0c;一种是Cache-Aside&#xff0c;一种是cache-through。也就是旁路缓存和缓存即数据源。 一般一种用于读&#xff0c;另一种用于读写。参考后台服务架构高性能设计之道。 最典型的Cache-Aside的样例&#xff1a; //读操作 da…

亚信安全并购亚信科技交易正式完成

亚信安全与亚信科技联合宣布&#xff0c;亚信安全正式完成对亚信科技的控股权收购&#xff0c;由此&#xff0c;规模近百亿的中国最大的软件企业之一诞生&#xff01;双方将全面实现公司发展战略&#xff0c;以及优势能力与资源的深度融合&#xff0c;形成业界独有的“懂网、懂…

MybatisPlus入门(十)MybatisPlus-逻辑删除和多记录操作

一、Mybatis-Plus 多记录操作 按照主键删除多条记录 List<Long> ids Arrays.asList(new Long[]{2,3}) userDao.deleteBatchIds(ids); 示例代码如下: Testvoid testDelete(){//删除指定多条数据List<Long> list new ArrayList<>();list.add(14025513424818…

【css】overflow: hidden效果

1. 不添加overflow: hidden 1.1 效果 上面无圆角 1.2 代码 <template><view class"parent"><view class"child1">child1</view><view class"child2">child2</view></view></template><…

「QT」几何数据类 之 QPolygonF 浮点型多边形类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid…

架构篇(04理解架构的演进)

目录 学习前言 一、架构演进 1. 初始阶段的网站架构 2. 应用服务和数据服务分离 3. 使用缓存改善网站性能 4. 使用应用服务器集群改善网站的并发处理能力 5. 数据库读写分离 6. 使用反向代理和CDN加上网站相应 7. 使用分布式文件系统和分布式数据库系统 8. 使用NoSQL和…

OpenCV基础05_GUI和PyMsql

目录 一、PySimpleGUI 1、布局和窗口 2、文本框组件 3、视频处理 4、图片处理 二、pymsql 1、数据库操作 2、数据采集 3、人脸识别 一、PySimpleGUI PySimpleGUI 是一个用于简化 GUI 编程的 Python 包&#xff0c;它封装了多种底层 GUI 框架&#xff08;如 tkinter、…

ModuleNotFoundError: No module named ‘_ssl‘ centos7中的Python报错

报错 ModuleNotFoundError: No module named ‘_ssl’ 解决步骤&#xff1a; 1.下载openssl wget https://www.openssl.org/source/openssl-3.0.7.tar.gz tar -zxvf openssl-3.0.7.tar.gz cd openssl-3.0.72.编译安装 ./config --prefix/usr/local/openssl make make install3…

外呼系统只需这 3 种功能,电销效率快速提升

在当今竞争激烈的商业环境中&#xff0c;电销团队面临着诸多挑战。如何提高电销效率&#xff0c;成为了企业关注的焦点。今天&#xff0c;小编就给大家介绍&#xff0c;沃创云三种外呼系统功能&#xff0c;让你的电销效率快速提升。 一、智能拨号功能 传统的电销方式中&#x…

18. Mouse 鼠标、KeyBoard 键盘和 Action 消息事件处理

在本节的例子中&#xff0c;会自定义很多UI控件实现不同的事件响应&#xff0c;如下图所示&#xff1a; IOKit 事件框架 事件流程 OS X的事件依赖 IOKit 框架&#xff0c;事件发生后首先会传递到IOKit框架中处理&#xff0c;然后通知Window Server服务层处理&#xff0c;由…

C# 实现对指定句柄的窗口进行键盘输入的实现

在C#中实现对指定句柄的窗口进行键盘操作&#xff0c;可以通过多种方式来实现。以下是一篇详细的指南&#xff0c;介绍如何在C#中实现这一功能。 1. 使用Windows API函数 在C#中&#xff0c;我们可以通过P/Invoke调用Windows API来实现对指定窗口的键盘操作。以下是一些关键的…

Spring Plugin与策略模式:打造动态可扩展的应用

目录 一、策略模式 二、Spring Plugin 2.1 Spring Plugin 实现策略模式开发 2.2 策略模式优缺点 三、Spring Plugin 原理 一、策略模式 策略模式是一种设计模式&#xff0c;它允许程序在运行中动态的选择不同的行为方式进行动态执行。策略模式的核心思想是将行为封装在一个个…

Word大珩助手:超大数字怎么读?35位数字?69位数字?

俄罗斯日前对谷歌开出了20000000000000000000000000000000000&#xff08;35位数字&#xff09;美元的罚款 这一数字远超全球GDP总和&#xff0c;消息一出很快就登上热搜。 面对这样一个庞大的数字&#xff0c;人们不禁好奇&#xff0c;这样的数字该如何读出来&#xff1f; …

asp.net文件防盗链

URLRewriter实现 可以参考下面的文章 代码 .net framework 新建asp.net framework的web项目&#xff0c;新建AntiTheftChainHandler using System.Web;namespace AntiTheftChainStu01.Handler {public class AntiTheftChainHandler : IHttpHandler{public bool IsReusable…