代码随想录阅读笔记-栈与队列【滑动窗口最大值】

题目

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

返回滑动窗口中的最大值。

进阶:

你能在线性时间复杂度内解决此题吗?

提示:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

思路 

这是使用单调队列的经典题目。难点是如何求一个区间里的最大值。

暴力方法,遍历一遍的过程中每次从窗口中再找到最大的数值,这样很明显是O(n × k)的算法。

有的人可能会想用一个大顶堆(优先级队列)来存放这个窗口里的k个数字,这样就可以知道最大的最大值是多少了, 但是问题是这个窗口是移动的,而大顶堆每次只能弹出最大值,我们无法移除其他数值,这样就造成大顶堆维护的不是滑动窗口里面的数值了。所以不能用大顶堆。

此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。

这个队列应该长这个样子:

class MyQueue {
public:void pop(int value) {}void push(int value) {}int front() {return que.front();}
};

每次窗口移动的时候,调用que.pop(滑动窗口中移除元素的数值),que.push(滑动窗口添加元素的数值),然后que.front()就返回我们要的最大值。

其实在C++中,可以使用 multiset 来模拟这个过程,文末提供这个解法仅针对C++,以下讲解我们还是靠自己来实现这个单调队列。然后再分析一下,队列里的元素一定是要排序的,而且要最大值放在出队口,要不然怎么知道最大值呢。但如果把窗口里的元素都放进队列里,窗口移动的时候,队列需要弹出元素。那么问题来了,已经排序之后的队列 怎么能把窗口要移除的元素(这个元素可不一定是最大值)弹出呢。

其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。那么这个维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来实现一个单调队列

不要以为实现的单调队列就是 对窗口里面的数进行排序,如果排序的话,那和优先级队列又有什么区别了呢。

来看一下单调队列如何维护队列里的元素。

动画如下:

239.滑动窗口最大值

对于窗口里的元素{2, 3, 5, 1 ,4},单调队列里只维护{5, 4} 就够了,保持单调队列里单调递减,此时队列出口元素就是窗口里最大元素。

此时大家应该怀疑单调队列里维护着{5, 4} 怎么配合窗口进行滑动呢?

设计单调队列的时候,pop,和push操作要保持如下规则:

  1. pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。

为了更直观的感受到单调队列的工作过程,以题目示例为例,输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3,动画如下:

239.滑动窗口最大值-2

那么我们用什么数据结构来实现这个单调队列呢?使用deque最为合适,关于deque的具体解释大家可以参考STL教程(五):C++ STL常用容器之deque - 知乎 (zhihu.com),我们就提到了常用的queue在没有指定容器的情况下,deque就是默认底层容器。

基于刚刚说过的单调队列pop和push的规则,代码不难实现,如下:

class MyQueue { //单调队列(从大到小)
public:deque<int> que; // 使用deque来实现单调队列// 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。// 同时pop之前判断队列当前是否为空。void pop(int value) {if (!que.empty() && value == que.front()) {que.pop_front();}}// 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。// 这样就保持了队列里的数值是单调从大到小的了。void push(int value) {while (!que.empty() && value > que.back()) {que.pop_back();}que.push_back(value);}// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。int front() {return que.front();}
};

这样我们就用deque实现了一个单调队列,接下来解决滑动窗口最大值的问题就很简单了,直接看代码吧。

C++代码如下:

class Solution {
private:class MyQueue { //单调队列(从大到小)public:deque<int> que; // 使用deque来实现单调队列// 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。// 同时pop之前判断队列当前是否为空。void pop(int value) {if (!que.empty() && value == que.front()) {que.pop_front();}}// 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。// 这样就保持了队列里的数值是单调从大到小的了。void push(int value) {while (!que.empty() && value > que.back()) {que.pop_back();}que.push_back(value);}// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。int front() {return que.front();}};
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {MyQueue que;vector<int> result;for (int i = 0; i < k; i++) { // 先将前k的元素放进队列que.push(nums[i]);}result.push_back(que.front()); // result 记录前k的元素的最大值for (int i = k; i < nums.size(); i++) {que.pop(nums[i - k]); // 滑动窗口移除最前面元素que.push(nums[i]); // 滑动窗口前加入最后面的元素result.push_back(que.front()); // 记录对应的最大值}return result;}
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(k)

再来看一下时间复杂度,使用单调队列的时间复杂度是 O(n)。

有的同学可能想了,在队列中 push元素的过程中,还有pop操作呢,感觉不是纯粹的O(n)。

其实,大家可以自己观察一下单调队列的实现,nums 中的每个元素最多也就被 push_back 和 pop_back 各一次,没有任何多余操作,所以整体的复杂度还是 O(n)。

空间复杂度因为我们定义一个辅助队列,所以是O(k)

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

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

相关文章

如何提升FFmpeg 1‰的转码性能

在8K视频编解码特别是解码部分&#xff0c;我做了一些优化工作&#xff0c;转码速度提升了50%以上。专家们评价曰&#xff1a;“主要围绕算法并行度的优化&#xff0c;属于算法性能优化的常规手段&#xff0c;在创新性和技术难度方面的体现较为一般”。评价过于犀利&#xff0c…

一文道破将bean注入到Spring中的几种方式

前言&#xff1a; 前两天有学妹问我如何将bean注入到Spring中&#xff0c;虽问题较简单&#xff0c;但还是写此文以告之。 在Java的Spring框架中&#xff0c;将bean注入到容器中是核心概念之一&#xff0c;这是实现依赖注入的基础。Spring提供了多种方式来将bean注入到容器中…

MySQL高可用解决方案――从主从复制到InnoDB Cluster架构

2024送书福利正式起航 关注「哪吒编程」&#xff0c;提升Java技能 文末送5本《MySQL高可用解决方案――从主从复制到InnoDB Cluster架构》 大家好&#xff0c;我是哪吒。 爱奇艺每天都为数以亿计的用户提供7x24小时不间断的视频服务。通过爱奇艺的平台&#xff0c;用户可以…

力扣:290. 单词规律

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 给定一种规律 pattern 和一个字符串 s &#xff0c;判断 s 是否遵循相同的规律。 这里的 遵循 指完全匹配&#xff0c;例如&#xff0c; pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律…

docker推拉时的数据交换详解

前言 docker用了这么久了, 有没有想过, 在执行docker push 和 docker pull命令的时候, 数据是如何传递的呢? 换句话说, 如果要实现一个镜像仓库, 针对推拉的服务, 如何实现接口呢? 根据OCI 分发规范文档 的描述, 已经对整个推拉过程中要调用的接口有描述了. 但是, 纸上学来…

CNN、Transformer、Uniformer之外,我们终于有了更高效的视频理解技术

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 视频理解因大量时空冗余和复杂时空依赖&#xff0c;同时克服两个问题难度巨大…

力扣每日一题 2024/3/24 零钱兑换

题目描述 用例说明 思路讲解 动态规划五步法 第一步确定dp数组的含义&#xff1a;dp[i]为凑到金额为i所用最少的硬币数量 第二步确定动态规划方程&#xff1a;凑足金额为j-coins[i]所需最少的硬币个数为dp[j-coins[i]]&#xff0c;那凑足金额为j所用的最少硬币数为dp[j-coin…

怎么将文件快速生成二维码?文件二维码的在线生成技巧

现在越来越多的人都开始通过二维码的方式来传递文件&#xff0c;将word、pdf、excel、pdf等格式的文件通过扫码的方式展示或者下载文件&#xff0c;这种方式有很多的优势&#xff0c;包括传播速度快成本低&#xff0c;只需要生成一张二维码图片&#xff0c;就可以让其他人能够同…

Prompt-RAG:在特定领域中应用的革新性无需向量嵌入的RAG技术

论文地址&#xff1a;https://arxiv.org/ftp/arxiv/papers/2401/2401.11246.pdf 原文地址&#xff1a;https://cobusgreyling.medium.com/prompt-rag-98288fb38190 2024 年 3 月 21 日 虽然 Prompt-RAG 确实有其局限性&#xff0c;但在特定情况下它可以有效地替代传统向量嵌入 …

QTableWidget删除单元格

如果单元格内有内容&#xff0c;可以使用函数selectedItems() 获取有内容行的一个链表 QList<QTableWidgetItem *> items ui->qtableWidget->selectedItems(); //选中有内容的行可选择有内容的行int count items.count();for(int i 0 ; i < count; i){ …

搭建vite项目

文章目录 Vite 是一个基于 Webpack 的开发服务器&#xff0c;用于开发 Vue 3 和 Vite 应用程序 一、创建一个vite项目二、集成Vue Router1.安装 vue-routernext插件2.在 src 目录下创建一个名为 router 的文件夹&#xff0c;并在其中创建一个名为 index.js 的文件。在这个文件中…

element-ui radio-group 组件源码分享

接着上篇的 radio 组件源码分享&#xff0c;继续探索 radio-group 源码部分的实现过程&#xff0c;主要从以下四个方面来讲解&#xff1a; 1、el-radio-group 页面结构 2、el-radio-group 组件属性 3、el-radio-group 组件方法 4、核心代码部分 一、页面结构&#xff0c;如…

docker 不同架构镜像融合问题解决

1、背景 docker 作为目前容器的标准之一&#xff0c;但是对于多种架构的平台的混合编译支撑不是很好。因此衍生了镜像融合&#xff0c;分别将多种不同的架构构建好&#xff0c;然后将镜像进行融合上传。拉取镜像的会根据当前系统的架构拉取不同的镜像&#xff0c;也可以通过 -…

Linux内核err.h文件分析

在阅读和编写内核相关的代码时&#xff0c;经常会看到IS_ERR、ERR_PTR等函数。这些函数在内核头文件的err.h中。以我服务器的代码为例&#xff0c;内核版本为5.15。 这个文件的代码如下&#xff1a; /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_ERR_H #define _L…

基于nodejs+vue在线作业管理系统的设计与实现python-flask-django-php

这种个性化的网络系统管理更重视相互协调和管理合作,能激发管理者的创造性和主动性,这对在线作业管理系统来说非常有益。 关键词&#xff1a;在线作业管理系统&#xff0c;nodejs语言&#xff0c;express框架&#xff0c; 前端技术&#xff1a;nodejsvueelementui, Express 框架…

易源堂梵仕哲品牌新品发布会

祥龙启新&#xff0c;非凡无际&#xff01;2024年3月16日&#xff0c;上海易源堂集团梵仕哲品牌新品发布会在有着“山水宁海,寿者之乡”称号的浙江宁海顺利召开&#xff1b;易源堂集团联合创始人集团副总经理李振雨、易源堂集团营销策划总监曹斌&#xff0c;易源堂副总经理姚军…

mysql 用户管理-账户管理

学习了《mysql 用户管理-权限表》。接着学习更常用的的账户管理。 2&#xff0c;账户管理 MySQL提供许多语句用来管理用户账号,这些语句可以用来管理包括登录和退出MySQL服务器、创建用户、删除用户、密码管理和权限管理等内容。MySQL 数据库的安全性&#xff0c;需要通过账户管…

注册马来西亚商标常见问题

马来西亚商标法于1983年9月1日正式生效。这部商标法废除了马来亚、沙巴和沙捞越三地区各自的商标法规和申请程序&#xff0c;使马来西亚有了一部统一商标法。此外&#xff0c;马来西亚有关商标的法规包括1983年9月1日同时生效的《1983年商标法实施细则》。在马来西亚&#xff0…

Xcode-双架构arm64 x86_64编译

要启用通用构建&#xff0c;在最新版本的 Xcode 中&#xff0c;请打开您的项目设置&#xff0c;然后依次选择&#xff1a; 1. “Build Settings” 选项卡。 2. 在顶部输入框中输入 “Architectures”。 3. 在 “Architectures” 下拉列表中选择 “Other”。 4. 在输入框中输入 …

国内git最新版本下载链接2.44

git官网地址:Git - Downloading Package (git-scm.com) 蓝奏云: ​​​​​​gGit-2.44.0-64-bit.exe - 蓝奏云 git仓库地址:git/git: Git Source Code Mirror - This is a publish-only repository but pull requests can be turned into patches to the mailing list via …