代码随想录笔记|C++数据结构与算法学习笔记-栈和队列(〇)|stack、queue、单调队列和优先级队列(priority_queue)、大顶堆和小顶堆

news2024/11/23 16:59:39

文章目录

  • stack容器
    • stack 基本概念
    • 常用接口
      • 构造函数
      • 赋值操作
      • 数据存取
      • 大小操作
  • queue容器
    • queue常用接口
      • 构造函数:
      • 赋值操作
      • 数据存取
      • 大小操作
  • 单调队列
    • 定义
    • 实现
      • 代码实现
    • 基本应用一:滑动窗口
      • 思路与算法
  • 优先级队列
    • 定义
      • 大顶堆(最大堆)、小顶堆(最小堆)
    • 实现
    • 基本操作
      • `push`和`emplace`
    • 基本应用一:滑动窗口
      • 思路与算法

stack容器

stack 基本概念

在这里插入图片描述
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为。
栈中进入数据称为 — 入栈 push

栈中弹出数据称为 — 出栈 pop

常用接口

构造函数

  • stack<T> stk; //stack采用模板类实现, stack对象的默认构造形式
  • stack(const stack &stk); //拷贝构造函数

赋值操作

  • stack& operator=(const stack &stk); //重载等号操作符

数据存取

  • push(elem) //向栈顶添加元素
  • pop(); //从栈顶移除元素
  • top(); //返回栈顶元素

大小操作

  • empty; //返回堆栈是否为空
  • size(); //返回栈的大小

queue容器

在这里插入图片描述
队列容器允许从一端新增元素,从另一端移除元素

队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为

队列中进数据称为 — 入队 push

队列中出数据称为 — 出队 pop

queue常用接口

构造函数:

  • queue<T> que; //queue采用模板类实现,queue对象的默认构造形式
  • queue(const queue &que); //拷贝构造函数

赋值操作

  • queue& operator=(const queue &que); //重载等号操作符

数据存取

  • push(elem); //往队尾添加元素
  • pop(); //从队头移除第一个元素
  • back(); //返回最后一个元素
  • front(); //返回第一个元素

大小操作

  • empty(); //判断堆栈是否为空
  • size(); //返回栈的大小

单调队列

定义

单调队列也是一种常用的数据结构,但是在C++中并没有这类数据结构的实现。

单调队列的单调在于其内部的元素始终按照一定的单调性(递增或递减)排列。

始终按照是什么意思呢?即在每次加入或者删除元素时都保持序列里的元素有序,即队首元素始终是最小值或者最大值,这个功能非常重要,单调队列我们就是使用的这个功能
这种数据结构通常用于解决滑动窗口类型的问题,可以在 O(1) 时间复杂度内给出当前窗口的最大值或最小值。

实现

在实现时,需要保证队列的单调性:对于一个单调递增的队列,新进入队列的元素如果小于队尾的元素,那么队尾的元素将会被移除,直到队列单调或者队列为空。这样,队头元素始终是当前窗口的最小值。单调递减队列则相反
例子如下所示:

1: 5
2: 8
3: 8 2
4: 8 4
5: 8 4 1

详细过程如下:

1.首先队列里面没有元素,5加进去。
2.第二个元素8大于队尾的元素,所以5要弹出去,8加进去。保持队首最大
3.第三个元素2小于队尾元素8,可以加进去,变为8 2
4.4大于队尾元素2,2弹出,4小于8,8不弹出,4加进去
5.1小于队尾元素4,1加进去,最后队列为8 4 1

代码实现

单调队列的实现通常使用双端队列(deque),它允许在队列的前端和后端都可以进行元素的添加和删除操作

#include <deque>
#include <vector>

template<typename T>
class MonotonicQueue {
private:
    std::deque<T> data;

public:
    // Push an element on the queue. Remove elements smaller than the incoming one
    // to maintain the monotonic property.
    void push(T val) {
        while (!data.empty() && data.back() < val) {
            data.pop_back();
        }
        data.push_back(val);
    }

    // Return the maximum element
    T max() const {
        return data.front();
    }

    // Pop an element from the queue
    void pop(T val) {
        if (!data.empty() && data.front() == val) {
            data.pop_front();
        }
    }
};

基本应用一:滑动窗口

文章链接
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

思路与算法

由于我们需要求出的是滑动窗口的最大值,如果当前的滑动窗口中有两个下标 ij,其中 i j 的左侧(i<j),并且 i 对应的元素不大于 j 对应的元素(nums[i]≤nums[j]),那么会发生什么呢?

当滑动窗口向右移动时,只要i还在窗口中,那么 j 一定也还在窗口中,这是 ij左侧所保证的。因此,由于 nums[j]的存在,nums[i] 一定不会是滑动窗口中的最大值了,我们可以将 nums[i]永久地移除

因此我们可以使用单调队列存储所有还没有被移除的下标。在单调队列中,这些下标按照从小到大的顺序被存储,并且它们在数组nums中对应的值是严格单调递减的。因为如果队列中有两个相邻的下标,它们对应的值相等或者递增,那么令前者为i,后者为 j,就对应了上面所说的情况,即 nums[i]会被移除,这就产生了矛盾。

当滑动窗口向右移动时,我们需要把一个新的元素放入队列中。为了保持队列的性质,我们会不断地将新的元素与队尾的元素相比较,如果前者大于等于后者,那么队尾的元素就可以被永久地移除,我们将其弹出队列。我们需要不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。

由于队列中下标对应的元素是严格单调递减的,因此此时队首下标对应的元素就是滑动窗口中的最大值。不过此时的最大值可能在滑动窗口左边界的左侧,并且随着窗口向右移动,它永远不可能出现在滑动窗口中了。因此我们还需要不断从队首弹出元素,直到队首元素在窗口中为止

链接:力扣官方题解

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        deque<int> q;
        for (int i = 0; i < k; ++i) {
            while (!q.empty() && nums[i] >= nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
        }

        vector<int> ans = {nums[q.front()]};
        for (int i = k; i < n; ++i) {
            while (!q.empty() && nums[i] >= nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
            while (q.front() <= i - k) {
                q.pop_front();
            }
            ans.push_back(nums[q.front()]);
        }
        return ans;
    }
};

优先级队列

定义

优先级队列是一种抽象数据类型,它支持普通队列的基本操作,如入队和出队。不过,在优先级队列中,每个元素都有一定的“优先级”,出队操作会移除具有最高优先级的元素,而不是最先进入队列的元素。这种队列通常用于任务调度、带优先级的待办事项管理等场合。

在 C++ 中,优先级队列通常通过使用二叉堆(特别是大顶堆或小顶堆)来实现,而且标准库 <queue> 中已经提供了模板类 std::priority_queue

std::priority_queue 是一个容器适配器,它提供了某种特定服务策略(默认为最大堆)排序的队列

std::priority_queue 默认情况下使用一个 std::vector 作为底层容器,并使用 std::less 作为比较函数,这意味着元素是按照严格弱序(默认为大顶堆,即最大的元素总是在队列前端)排序的。

#include <iostream>
#include <queue>

// 默认情况下,C++使用最大堆实现优先级队列
std::priority_queue<int> max_heap;

// 使用最小堆实现优先级队列
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;

那么问题来了,大顶堆和小顶堆又是什么呢?

大顶堆(最大堆)、小顶堆(最小堆)

堆的概念:堆具有结构性,也就是它是采用数组表示的完全二叉树。堆还具有有序性,也就是根节点大于子节点(或者小于子节点)

通过根节点大于子节点(或小于子节点),又可以将堆分为大顶堆和小顶堆

大顶堆:又称为最大堆,也就是树中所有父节点都要大于或等于子节点

小顶堆:又称为最小堆,也就是树中所有父节点都要小于或等于子节点

原文链接

实现

#include <iostream>
#include <queue>

// 默认情况下,C++使用最大堆实现优先级队列
std::priority_queue<int> max_heap;

// 使用最小堆实现优先级队列
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;

基本操作

  • top:返回优先队列中具有最高优先级的元素。对于最大堆实现的优先队列,这将是最大的元素;对于最小堆实现,则是最小的元素。
  • push:向优先队列中添加一个元素。新元素的位置将根据其优先级与其他元素的比较结果来确定。
  • pop: 移除具有最高优先级的元素。在最大堆优先队列中,这通常是最大元素;在最小堆中,是最小元素。
  • empty: 检查优先队列是否为空。如果队列为空,返回 true;否则返回 false
  • size: 返回优先队列中元素的个数。
  • emplace:这个方法可以用来直接在优先队列的底层容器中就地构造一个新元素,这样可以避免额外的拷贝或移动操作。

pushemplace

push 方法相比,emplace 方法可以更高效地添加元素,特别是当队列中的对象较大或拥有非平凡的构造函数时。emplace 方法接受与元素构造函数相同的参数,并且在队列的适当位置直接构造对象。

#include <iostream>
#include <queue>
#include <string>

int main() {
    std::priority_queue<std::string> pq;

    // 直接在优先队列中构造元素
    pq.emplace("orange");
    pq.emplace("strawberry");
    pq.emplace("apple");

    std::cout << "The top element is " << pq.top() << '\n';

    return 0;
}

在这个例子中,emplace 用于直接在优先队列中构造 std::string 对象。这避免了创建临时 std::string 对象并将它们推入队列的需要。这样不仅提高了效率,而且也使代码更加简洁。

基本应用一:滑动窗口

文章链接
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

思路与算法

对于「最大值」,我们可以想到一种非常合适的数据结构,那就是优先队列(堆),其中的大根堆可以帮助我们实时维护一系列元素中的最大值

初始时,我们将数组 nums的前 k 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除

我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),表示元素 num在数组中的下标为 index

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        priority_queue<pair<int, int>> q;	//pair<int, int>将数组的值和对应的索引捆绑在一起
        for (int i = 0; i < k; ++i) {
            q.emplace(nums[i], i);
        }
        vector<int> ans = {q.top().first};
        for (int i = k; i < n; ++i) {
            q.emplace(nums[i], i);	//每次迭代将新元素和其索引加入优先级队列中。
            while (q.top().second <= i - k) {
            //在这个循环中,移除所有不再属于当前滑动窗口的元素
            //q.top().second 是队列顶部元素的索引,
            //如果它小于或等于 i - k,那么这个元素就不在窗口 [i - k + 1, i] 范围内,
            //因此需要将其从队列中弹出。
                q.pop();
            }
            ans.push_back(q.top().first);
        }
        return ans;
    }
};

链接:力扣官方题解

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

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

相关文章

1.5-数组-059. 螺旋矩阵 II★★

59. 螺旋矩阵II ★★ 力扣题目链接&#xff0c;给你一个正整数 n &#xff0c;生成一个包含 1 到 n 2 n^2 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。1 < n < 20 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,…

【大数据运维】minio 常见shell操作

文章目录 1. 安装2. 入门操作3. 命令帮助 1. 安装 下载 https://dl.min.io/client/mc/release/linux-amd64/ 赋权与使用 cp mc /usr/bin && chmod x /usr/bin/mc ./mc --help 2. 入门操作 # 添加minio到mc mc config host add minio_alias_name endpoint_adress …

成都市酷客焕学新媒体科技有限公司:实现品牌的更大价值!

成都市酷客焕学新媒体科技有限公司专注于短视频营销&#xff0c;深知短视频在社交媒体中的巨大影响力。该公司巧妙地将品牌信息融入富有创意和趣味性的内容中&#xff0c;使观众在轻松愉悦的氛围中接受并传播这些信息。凭借独特的创意和精准的营销策略&#xff0c;成都市酷客焕…

2024 蓝桥打卡Day25

CCFCSP算法练习 202305-1 重复局面 202305-2 矩阵运算 202303-1 田地丈量 202303-2 垦田计划

C++王牌结构hash:哈希表闭散列的实现与应用

一、哈希概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在查找一个元素 时&#xff0c;必须要经过关键码的多次比较。顺序查找时间复杂度为O(N)&#xff0c;平衡树中为树的高度&#xff0c;即O(log n)&#xff0c;搜索的效率…

尾矿库在线安全监测:提升矿山安全水平

在矿山安全领域&#xff0c;尾矿库的安全管理尤为关键。尾矿库作为矿山生产链条的重要环节&#xff0c;其稳定性不仅关系到生产活动的持续进行&#xff0c;更直接影响着周边环境和人民群众的生命财产安全。因此&#xff0c;尾矿库的安全监测显得尤为重要。近年来&#xff0c;随…

全球首个“AI程序员”Deven能替代程序员吗?过了面试却不一定适合职场

制造Devin的公司&#xff0c;是一家叫Cognition的10人初创公司&#xff0c;才成立不到2个月。 一、引言 一家成立不到两个月但拥有十名天才工程师的初创公司Cognition&#xff0c;搞了一个引爆科技圈的大动作。 他们推出了一款名为Devin的人工智能&#xff08;AI&#xff09;助…

【Python函数和类2/6】函数的参数

目录 目标 为函数设置参数 传递实参 关键字实参 关键字实参的顺序 位置实参 常见错误 缺少实参 位置实参的顺序 默认值形参 参数的优先级 默认值形参的位置 总结 目标 上篇博客中&#xff0c;我们在定义函数时&#xff0c;使用了空的括号。这表示它不需要任何信息就…

IDEA2021.1.2破解无限试用30天破解IDEA

安装包下载 IDEA安装包&#xff1a;Other Versions - IntelliJ IDEA破解包下载&#xff1a;文件 密码:c033 开始激活 IDEA 2021.1.3 运行, 中间会先弹出一个注册框&#xff0c;我们勾选 Evaluate for free, 点击 Evaluate&#xff0c; 先试用30天: 注意&#xff0c;如果没有…

Leo赠书活动-22期 【大模型在金融行业的应用场景和落地路径】文末送书

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 赠书活动专栏 ✨特色专栏&#xff1a;…

对form表单对象中数组中的字段进行校验的方法

当对form表单中&#xff0c;数组readings中的字段进行校验时&#xff0c;prop和rules绑定要写成动态的&#xff0c;如下代码 <div v-for"(item,index) in form.readings"><el-form-item label"上次读数" > <!--prop"scds"-->…

威联通安装Kafka

最近在学习 Kafka 的知识&#xff0c;遇到一些问题网上搜到的信息不全。想要在本地安装一个 Kafka 进行验证&#xff0c;想到了之前买的 Nas 就开始折腾。 用 Docker 的方式安装 Kafka 现在的 Nas 很多都支持 Docker&#xff0c;我买的也支持。威联通的 Docker 叫 Container S…

Ubuntu通过分用户进行多版本jdk配置

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; linux安装jdk参考&#xff1a;http://t.csdnimg.cn/TeECj 出发点&#xff1a;最新的项目需要用jdk17来编译&#xff0c;就把服务器的jdk版本升级到了17&#xff0c;但是有一些软件例如nexus还需要jdk1.8进…

【算法】第一篇 外观数列

导航 1. 简介2. 生成规则3. 代码演示1. 简介 外观数列是指数列中的每一项都是描述前一项的外观或者外貌。它通常由初始项开始,通过描述前一项的外观来生成下一项。外观数列最初由John H. Conway在1969年发现,并在他的著作《外貌数列和自动机理论》(The Construction of Look…

数据持久化第二课-C#高级特性

数据持久化第二课-C#高级特性 一.预习笔记 1.委托 1-1.委托的概述与声明 1-1-1&#xff1a;委托是一种引用类型&#xff0c;表示对具有特定参数列表和返回类型的方法的引用 1-1-2&#xff1a;可以通过委托实例调用方法。也可以使用委托将方法作为参数传递给其他方法 1-1-3…

《VulnHub》Lampião:1

title: 《VulnHub》Lampio&#xff1a;1 date: 2024-03-28 21:37:49 updated: 2024-03-28 21:37:50 categories: WriteUp&#xff1a;Cyber-Range excerpt: 关键技术&#xff1a;主机发现&#xff0c;端口扫描、服务探测、操作系统探测&#xff0c;对开放的端口探测漏洞&#x…

Vue-Electron配置及踩坑

前言 大道至简。太复杂的教程不看。 本篇将记述我创建好Vue3项目之后&#xff0c;用Electron把页面呈现出来的整个过程。会记录一些踩坑。 首先&#xff0c;Electron官网可以参考。但是它只是作出了一个普通的html结构该如何用Electron呈现出来&#xff0c;vue的配置有一些变…

防止恶意软件和网络攻击的简单贴士

如今&#xff0c;缺少互联网的生活是难以想象的。然而&#xff0c;互联网的匿名性导致了网络攻击和恶意软件很猖獗。恶意软件会损坏我们的设备、窃取个人数据&#xff0c;并导致金钱损失。因此&#xff0c;保护计算机免受这些威胁显得至关重要。 一、确保操作系统和软件是最新版…

【深度学习目标检测】二十五、基于深度学习的花卉分类系统-含数据集、GUI和源码(python,yolov8)

设计花卉分类系统的原因主要有以下几点&#xff1a; 组织和识别&#xff1a;分类系统有助于组织和识别大量的花卉品种。通过将花卉按照特定的标准进行分类&#xff0c;可以更容易地找到、识别和区分不同的花卉。 科学研究&#xff1a;分类系统为科学家提供了研究花卉的基础框架…

C++从入门到精通——缺省参数

缺省参数 前言一、缺省参数概念二、缺省参数分类位置参数的缺省参数全缺省参数半缺省参数 关键字参数的缺省参数函数指针的缺省参数lambda表达式 三、缺省参数的具体代码展示main.cpp 前言 缺省参数是在函数定义时指定的默认值&#xff0c;当调用函数时未提供该参数的值时&…