【C++】STL - Stack - Queue - PriorityQueue使用和模拟实现

news2024/12/24 3:29:09

 🐱作者:傻响
🐱专栏:《数据结构_STL》
🔥格言:你只管努力,剩下的交给时间!

                                                         

目录

Stack介绍

模拟实现

队列

Queue介绍

常用的函数接口介绍

模拟实现

优先级队列

Priority queue介绍

Priority queue函数接口介绍

Priority queue模拟实现

Priority queue增加仿函数/函数对象


Stack介绍

  1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行 元素的插入与提取操作。

  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定 的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。

  3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下 操作:

  • empty:判空操作

  • back:获取尾部元素操作

  • push_back:尾部插入元素操作

  • pop_back:尾部删除元素操作

  1. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque。

  

常用的函数接口介绍

  

模拟实现

采用设计模型:适配器模式思想

#include<iostream>
#include<deque>
using namespace std;
​
namespace ShaXiang
{
    // 设计模式:
    // 适配器模式:将已经有的东西,进行封装转换出你想要的东西.
    template<class T, class Container = deque<T>>
    class Stack
    {
    private:
        Container _container;
    public:
        // 入栈
        void Push(const T& val)
        {
            _container.push_back(val);
        }
        // 删除数据
        void Pop()
        {
            _container.pop_back();
        }
        // 读取栈顶数据
        const T& Top()
        {
            return _container.back();
        }
        // 栈大小
        size_t Size()
        {
            return _container.size();
        }
        // 判断栈是否为空
        bool Empty()
        {
            return _container.empty();
        }
    };
}

队列

Queue介绍

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端 提取元素。

  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的 成员函数来访问其元素。元素从队尾入队列,从队头出队列。

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

  • empty:检测队列是否为空

  • size:返回队列中有效元素的个数

  • front:返回队头元素的引用

  • back:返回队尾元素的引用

  • push_back:在队列尾部入队列

  • pop_front:在队列头部出队列

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

  

常用的函数接口介绍

  

模拟实现

采用设计模型:适配器模式思想

#include<iostream>
#include<deque>
using namespace std;
​
namespace ShaXiang
{
    // 设计模式:
    // 适配器模式:将已经有的东西,进行封装转换出你想要的东西.
    template<class T, class Container = deque<T>>
    class Queue
    {
    public:
        // Push入
        void Push(const T& val)
        {
            _container.push_back(val);
        }
        // Pop出
        void Pop()
        {
            _container.pop_front();
        }
        // 返回头数据
        const T& Front()
        {
            return _container.front();
        }
        // 返回尾数据
        const T& Back()
        {
            return _container.back();
        }
        // 列大小
        size_t Size()
        {
            return _container.size();
        }
        // 判断列是否为空
        bool Empty()
        {
            return _container.empty();
        }
    private:
        Container _container;
    };
}

上面的Stack和Queue是适配了list和vector的底层实现的,但是vector和list是不完美的。

  • vector的缺点:头部中部插入删除效率慢,扩容消耗时间。

  • list的缺点:不支持随机访问,CPU高速缓存命中低。

deque完美的融合,兼具vector和list的优点,但是下标随机访问,有一定的消耗,没有vector快,中间插入删除也有一定的消耗,相比list的中间插入删除,不够极致,没有list快。

优先级队列

Priority queue介绍

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。

  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元 素)。

  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特 定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。

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

  • empty():检测容器是否为空

  • size():返回容器中有效元素个数

  • front():返回容器中第一个元素的引用

  • push_back():在容器尾部插入元素

  • pop_back():删除容器尾部元素

  1. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指 定容器类,则使用vector。

  2. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作。

Priority queue函数接口介绍

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

  

默认优先级为大堆:

#include<iostream>
#include<queue>
using namespace std;
​
int main(){
    priority_queue<int> pq;
    pq.push(3);
    pq.push(2);
    pq.push(1);
    pq.push(4);
    pq.push(5);
​
    while (!pq.empty()){
        cout << pq.top() << " ";
        pq.pop();
    }
    cout << endl;
​
    return 0;
}
​
// 打印结果:5 4 3 2 1

将默认优先级改为小堆:

#include<iostream>
#include<queue>
#include<functional>
using namespace std;
​
int main(){
    priority_queue<int,vector<int>, greater<int>> pq;
    pq.push(3);                     // less 大堆
    pq.push(2);                     // greater 小堆
    pq.push(1);
    pq.push(4);
    pq.push(5);
​
    while (!pq.empty()){
        cout << pq.top() << " ";
        pq.pop();
    }
    cout << endl;
​
    return 0;
}
​
// 打印结果:1 2 3 4 5

Priority queue模拟实现

采用设计模型:适配器模式思想

#pragma once
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
​
namespace ShaXiang
{
    // 设计模式:适配器模式
    template<class T, class Container = vector<T>>
    class PriorityQueue
    {
    private:
        Container _container;
​
    public:
        // 无参构造函数.
        PriorityQueue()
        {}
​
        // 迭代器构造函数.
        template<class InputIterator>
        PriorityQueue(InputIterator first, InputIterator last)
            : _container(first,last)
        {
            for (int i = (int(_container.size()) - 1 - 1) / 2; i >= 0; --i)
            {
                AdjustDown(i);
            }
        }
​
    public:
        // 堆->向上调整.
        void AdjustUp(size_t child) {
            // 求出父亲节点.
            size_t parent = (child - 1) / 2;
            while (child > 0) {
                if (_container[child] > _container[parent]) {
                    swap(_container[child], _container[parent]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else {
                    break;
                }
            }
        }
​
        // 堆->向下调整.
        void AdjustDown(size_t parent) {
            // 模糊算法:找最小的孩子.
            size_t minChild = parent * 2 + 1;
            while (minChild < _container.size()) {
                if (minChild + 1 < _container.size() && _container[minChild + 1] > _container[minChild]) {
                    ++minChild;
                }
​
                if (_container[minChild] > _container[parent]) {
                    swap(_container[minChild], _container[parent]);
                    parent = minChild;
                    minChild = parent * 2 + 1;
                }
                else {
                    break;
                }
                    
            }
        }
​
        // 插入数据.
        void Push(const T& val) {
            _container.push_back(val);
            AdjustUp(_container.size() - 1);
        }
​
        // 删除数据.
        void Pop() {
            swap(_container[0], _container[_container.size() - 1]);
            _container.pop_back();
            AdjustDown(0);
        }
        // 访问栈顶数据.
        const T& Top() const {
            return _container[0];
        }
        // 判断是否为空
        bool Empty() const {
            return _container.empty();
        }
        // 获取大小
        size_t Size() const {
            return _container.size();
        }
    };
}

Priority queue增加仿函数/函数对象

仿函数和函数对象实际是一个类。

我们可以使用仿函数/函数对象来改造一个冒泡排序看一下效果:

#pragma once
#include <iostream>
#include <algorithm>
using namespace std;
​
namespace ShaXiang {
    // 仿函数/函数对象 - 类
    template<class T>
    class Less {
    public:
        const bool operator()(const T& a, const T& b) const {
            return a < b;
        }
​
    };
​
    template<class T>
    class Greater{
    public:
        const bool operator()(const T& a, const T& b) const {
            return a > b;
        }
    };
}
​
// 使用仿函数改造一下冒泡排序.
template<class T, class Compare>
void BubbleSort(T* data, size_t n, Compare compare) {
    int flag = 0;
    for (size_t i = 0; i < n ; ++i) {
        for (size_t j = 1; j < n - i; ++j) {
            if (compare(data[j], data[j-1])) {
                swap(data[j - 1], data[j]);
                flag = 1;
            }   
        }
​
        if (flag == 0)
            break;
    }
}
​
int main() {
    // ShaXiang::Less<int> less;        // 单一调用可以使用匿名对象.
    // ShaXiang::Greater<int> greater;
    int arr[] = { 1,4,2,6,9,3,0,7 };
​
    BubbleSort<int>(arr, sizeof(arr) / sizeof(arr[0]), ShaXiang::Less<int>());
    for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
​
    BubbleSort<int>(arr, sizeof(arr) / sizeof(arr[0]), ShaXiang::Greater<int>());
    for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
​
    return 0;
}
仿函数和函数高级玩法。

这里用日期类在试一下(写出自己指定的仿函数):

#include <iostream>
#include <queue>
using namespace std;
​
class Date {
private:
    int _year;
    int _month;
    int _day;
​
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day)
    {}
​
public:
    friend ostream& operator<<(ostream& out, const Date& d)
    {
        out << d._year << "-" << d._month << "-" << d._day;
        return out;
    }
​
public:
    bool operator>(const Date& d) const {
        return (_year > d._year)
            || (_year == d._year && _month > d._month)
            || (_year == d._year && _month == d._month && _day > d._day);
    }
​
    bool operator<(const Date& d) const {
        return (_year < d._year)
            || (_year == d._year && _month < d._month)
            || (_year == d._year && _month == d._month && _day < d._day);
    }
};
​
// 仿函数/函数对象 - 类
template<class T>
class Date_Less {
public:
    bool operator()(const T& d1, const T& d2) const {
        return *d1 < *d2;
    }
​
};
​
template<class T>
class Date_Greater {
public:
    bool operator()(const T& d1, const T& d2) const {
        return *d1 > *d2;
    }
};
​
void TestPriorityQueue()
{
    // 大堆
    priority_queue<Date> q1;
    q1.push(Date(2018, 10, 29));
    q1.push(Date(2018, 10, 28));
    q1.push(Date(2018, 10, 30));
    cout << q1.top() << endl;
    // 小堆
    priority_queue<Date, vector<Date>, greater<Date>> q2;
    q2.push(Date(2018, 10, 29));
    q2.push(Date(2018, 10, 28));
    q2.push(Date(2018, 10, 30));
    cout << q2.top() << endl;
​
    // 大堆
    priority_queue<Date*, vector<Date*>, Date_Less<Date*>> q3;
    q3.push(new Date(2018, 10, 29));
    q3.push(new Date(2018, 10, 28));
    q3.push(new Date(2018, 10, 30));
    cout << *q3.top() << endl;
    // 小堆
    priority_queue<Date*, vector<Date*>, Date_Greater<Date*>> q4;
    q4.push(new Date(2018, 10, 29));
    q4.push(new Date(2018, 10, 28));
    q4.push(new Date(2018, 10, 30));
    cout << *q4.top() << endl;
}

嵌入Priority queue中

#pragma once
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
​
namespace ShaXiang
{
    // 仿函数/函数对象 - 类
    template<class T>
    class PriorityQueue_Less {
    public:
        const bool operator()(const T& a, const T& b) const {
            return a < b;
        }
​
    };
​
    template<class T>
    class PriorityQueue_Greater {
    public:
        const bool operator()(const T& a, const T& b) const {
            return a > b;
        }
    };
​
​
    // 设计模式:适配器模式
    template<class T, class Container = vector<T>, class Compare = PriorityQueue_Less<T>>
    class PriorityQueue
    {
    private:
        Container _container;
​
    public:
        // 无参构造函数.
        PriorityQueue()
        {}
​
        // 迭代器构造函数.
        template<class InputIterator>
        PriorityQueue(InputIterator first, InputIterator last)
            : _container(first,last)
        {
            for (int i = (int(_container.size()) - 1 - 1) / 2; i >= 0; --i)
            {
                AdjustDown(i);
            }
        }
​
    public:
        // 堆->向上调整.
        void AdjustUp(size_t child) {
            // 使用仿函数/函数对象.
            Compare compare;
​
            // 求出父亲节点.
            size_t parent = (child - 1) / 2;
            while (child > 0) {
                if (compare(_container[parent], _container[child])) {
                    swap(_container[child], _container[parent]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else {
                    break;
                }
            }
        }
​
        // 堆->向下调整.
        void AdjustDown(size_t parent) {
            // 使用仿函数/函数对象.
            Compare compare;
​
            // 模糊算法:找最小的孩子.
            size_t minChild = parent * 2 + 1;
            while (minChild < _container.size()) {
                if (minChild + 1 < _container.size() && compare(_container[minChild], _container[minChild + 1])) {
                    ++minChild;
                }
​
                if (compare(_container[parent], _container[minChild])) {
                    swap(_container[minChild], _container[parent]);
                    parent = minChild;
                    minChild = parent * 2 + 1;
                }
                else {
                    break;
                }
                    
            }
        }
​
        // 插入数据.
        void Push(const T& val) {
            _container.push_back(val);
            AdjustUp(_container.size() - 1);
        }
​
        // 删除数据.
        void Pop() {
            swap(_container[0], _container[_container.size() - 1]);
            _container.pop_back();
            AdjustDown(0);
        }
        // 访问栈顶数据.
        const T& Top() const {
            return _container[0];
        }
        // 判断是否为空
        bool Empty() const {
            return _container.empty();
        }
        // 获取大小
        size_t Size() const {
            return _container.size();
        }
    };
}

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

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

相关文章

【docker18】docker容器之CAdvisor+InfluxDB+Granfana

1.原生命令 1.1操作 命令&#xff1a; docke stats 1.2问题 通过docker stats命令可以很方便的看到当前宿主机上所有容器的CPU&#xff0c;内存以及网络流量控制等数据&#xff0c;一般的小公司够用了。 但是&#xff0c;docker stats统计结果只能是当前宿主机的全部容器&am…

669. 修剪二叉搜索树

669. 修剪二叉搜索树 难度中等 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&#xff0c…

SourceTree使用方法总结

SourceTree使用方法总结 SourceTree使用总结 添加仓库 mac下从url克隆&#xff1a; windows下从url克隆&#xff1a; 抓取、获取分支信息 抓取&#xff08;mac下的名字&#xff09;获取&#xff08;Windows下的名字&#xff09;指获取服务端git库的变更信息&#xff0c;比如…

基于FPGA的UDP 通信(五)

引言 前文链接&#xff1a; 基于FPGA的UDP 通信&#xff08;一&#xff09; 基于FPGA的UDP 通信&#xff08;二&#xff09; 基于FPGA的UDP 通信&#xff08;三&#xff09; 基于FPGA的UDP 通信&#xff08;四&#xff09; 本文基于FPGA设计千兆以太网通信模块UDP数据发…

12.I/O复用

I/O复用 多进程方式跳过 基于I/O复用的服务器端 接下来讨论并发服务器实现方法的延伸。如果有读者已经跳过第10章和第11章&#xff0c;那就只需把本章内容当做并发服务器实现的第一种方法即可。将要讨论的内容中包含一部分与多进程服务器端的比较&#xff0c;跳过第10章和第…

Android WebView中H5调用Android原生方法

最近做项目&#xff0c;使用webView看一些网页&#xff0c;和网页开发一起找什么方法进行交互&#xff0c;还好解决&#xff0c;分享一下经验。 对于webView的使用就不写了&#xff0c;百度大法好&#xff0c;主要是交互方面&#xff0c;对WebView增加以下代码&#xff1a; bi…

五个了解自己天赋优势的分析工具(一)霍兰德兴趣测试

霍兰德兴趣测试 霍兰德职业兴趣自测&#xff08;Self-Directed Search&#xff09;是由美国职业指导专家霍兰德&#xff08;John Holland&#xff09;根据他本人大量的职业咨询经验及其职业类型理论编制的测评工具。 霍兰德认为&#xff0c;个人职业兴趣特性与职业之间应有一…

74、Beyond RGB: Scene-Property Synthesis with Neural Radiance Fields

简介 List item 论文地址&#xff1a;http://arxiv-export3.library.cornell.edu/abs/2206.04669v1 利用隐式三维表示和神经渲染的最新进展&#xff0c;从综合模型的角度提供了一种新的场景理解方法&#xff0c;能够从新颖的视点渲染照片逼真的RGB图像&#xff0c;而且还能够…

我们怎样才能过好这一生?

文章目录1. 日拱一卒&#xff0c;功不唐捐1.1 适当的时候给自己一个奖励1.2 一个人可能走的更快&#xff0c;但一群人才能走的更远1.3 通过一些事情去逼自己一把1.4 从真理中去感悟1.5 当你面临绝路时2. 梦想的意义不在于实现3. 孤独4. 烦恼5. 别总说来日方长6. 忍和韧性7. 事情…

低成本搭建一台家庭存储服务器:前篇

本篇文章&#xff0c;记录搭建备份服务器的过程。 写在前面 今年考虑专门搭建一台用于数据备份的机器&#xff0c;一来今年外出的需求比较多&#xff0c;历史的设备已经用了几年了&#xff0c;需要有更新的设备来“接力”&#xff1b;二来也想验证方案的靠谱程度&#xff0c;…

k8s之ingress实战小栗子

写在前面 本文接k8s之ingress 。 本文看一个基于ingress作为流量入口的实战例子&#xff0c;架构图如下&#xff1a; 接下来详细看下。 1&#xff1a;部署MariaDB 首先我们需要定义MariaDB使用的configmap&#xff0c;如下&#xff1a; apiVersion: v1 kind: ConfigMap meta…

1587_AURIX_TC275_SMU的部分寄存器3

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) SMU的章节&#xff0c;剩下的部分全都是寄存器了&#xff0c;没有太多需要特别关注的。因此&#xff0c;接下来选择性整理&#xff0c;完成整个SMU的文档学习整理。 这一页是上一份笔记的…

05_FreeRTOS中断管理

目录 什么是中断 中断相关寄存器 源码实验 什么是中断 简介:让CPU打断正常运行的程序,转而去处理紧急的事件(程序) ,就叫中断。 举例:上课可以比做CPU正常运行的程序,上厕所可以比做中断程序。 中断执行机制,可简单概括为三步: 中断请求:外设产生中断请求(GPIO外部中断、…

【精品】k8s(Kubernetes)cka由基础到实战学法指南

轻松快速学会k8s四招 图1 k8s四招 学完本篇,您会获得什么惊喜? 从初学k8s,到帮助别人学会的过程中,发现朋友们和我,并非不努力,而是没有掌握更好的方法。有方法可让我们学的更快更轻松,这篇文章,以一个networkpolicy的题目,来逐步讲解,帮助大家建立一种,自己可以根…

Java基础语法

文章目录Java 基础语法一、注释1. 注释介绍2. 注释分类3. 注释颜色二、关键字1. 关键字介绍2. 所有关键词三、字面量四、变量1. 变量2. Debug 工具1&#xff09;如何加断点&#xff1f;2&#xff09;如何开启 Debug 运行&#xff1f;3&#xff09;点哪里 ?4&#xff09;看哪里…

ElasticSearch架构之整合ELK

前言本篇文章主要是说ElasticSearch对Logstash、FileBeat、Kibana整合形成ELK的架构&#xff0c;为什么需要整合这个架构呢&#xff1f;一个很重要的原因就是我们开发过程中有相当多的日志需要进行查看&#xff0c;如果我们要查找一个问题需要到多台服务器进行查看那是相当麻烦…

【Java基础知识 4】Java数据类型之间的转换、运算符

本文已收录专栏 &#x1f332;《Java进阶之路》&#x1f332; 目录 &#x1f334;基本数据类型 &#x1f343;01、布尔 &#x1f343;02、byte &#x1f343;03、short &#x1f343;04、int &#x1f343;05、long &#x1f343;06、float &#x1f343;07、double …

C生万物 | 详解程序环境和预处理【展示程序编译+链接全过程】

&#x1f451;作者主页&#xff1a;Fire_Cloud_1 &#x1f3e0;学习社区&#xff1a;烈火神盾 &#x1f517;专栏链接&#xff1a;万物之源——C 文章目录一、程序的翻译环境和执行环境二、详解编译链接1、前言小知识&#x1f50d;2、翻译环境【important】2.1 编译① 预编译【…

【LeetCode每日一题】【2023/1/15】2293. 极大极小游戏

文章目录2293. 极大极小游戏方法1&#xff1a;双指针2293. 极大极小游戏 LeetCode: 2293. 极大极小游戏 简单\color{#00AF9B}{简单}简单 给你一个下标从 0 开始的整数数组 nums &#xff0c;其长度是 2 的幂。 对 nums 执行下述算法&#xff1a; 设 n 等于 nums 的长度&#x…

Windows 10的子系统不是非Ubuntu不可

Ubuntu 的制造商 Canonical 早已和微软进行合作&#xff0c;让我们体验了极具争议的 Bash on Windows。外界对此也是褒贬不一&#xff0c;许多 Linux 重度用户则是质疑其是否有用&#xff0c;以及更进一步认为 Bash on Windows 是一个安全隐患。 Unix 的 Bash 是通过 WSL&#…