【算法与数据结构】单调队列

目录

单调队列

使用单调队列维护滑动窗口

具体过程:

代码实现: 

复杂度分析:

使用单调队列优化动态规划

例题 


单调队列

单调队列(deque)是一种特殊的队列,队列中的元素始终严格递增或者递减排列。这样就可以保证队头元素始终是最大值或者最小值。

【问题引入】

有一个数组,长度为n,给你一个区间[left,right],求出该区间内的最小值或者最大值。

我们如果进行普通的遍历,最坏的情况下时间复杂度是O(N),遍历整个数组。

而我们如果用单调队列来维护这段区间,始终保持队列的单调性,就可以在O(1)的时间内找到该区间的最大值或者最小值,就是队头元素

【结论】

所以单调队列的核心用途是高效维护动态窗口的极值(最大值或最小值)。

那么对于一些动态规划,如转移方程需要进行一段区间的最值计算,可以使用单调队列优化。


使用单调队列维护滑动窗口

题目链接:239. 滑动窗口最大值 - 力扣(LeetCode)

当窗口形成后,我们需要找到这段窗口中的最大值,暴力的方法就是对这段区间进行遍历,每个元素进行比较,选出最大值。这样做时间复杂度为O(N^2),效率太低。

使用单调队列优化:单调队列维护该窗口,队头元素为最大元素。时间复杂度为O(N)。

具体过程:

  • 创建一个单调对列,维护该队列的递减性,以保证对头元素为窗口中的最小值
  • 对于该单调队列,存放元素的下标,按值递减排列。新来一个元素(也就是滑动窗口右移一步),需要判断对头元素是否还在窗口内,所以记录下标,便于判断对头元素是否还在窗口中,如果不在,删除对头元素。
  • 新来一个元素(也就是滑动窗口右移一步),每次都需要比较队尾元素与当前元素的大小,我们维护的是递减队列,如果队尾元素大于当前元素,则将当前元素的下标直接加入队列;如果队尾元素小于当前元素,则将队尾元素删除,直到队尾元素大于当前元素,再将当前元素下标加入队列,保持队列的递减性。
  • 窗口形成后,统计结果即可。队头元素就是最大元素。

代码实现: 

class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {//双端队列 存储数组索引deque<int> dq;vector<int> res;for(int i=0;i<nums.size();i++){//移除超出范围的队首元素while(!dq.empty()&&dq.front()<=i-k)dq.pop_front();//维护队列递减性(从队头到队尾),移除所有小于当前元素的队尾元素 while(!dq.empty()&&nums[dq.back()]<nums[i])dq.pop_back();//当前元素入队列dq.push_back(i);//窗口形成后,统计结果,队头元素就是最大元素if(i>=k-1)res.push_back(nums[dq.front()]);}return res;}
};

 上面的写法是使用库中的deque,还可以使用数组来模拟:


#include<iostream>
using namespace std;
const int N = 1e6 + 5;int a[N], q[N];
int n, k;int main()
{cin >> n >> k;for (int i = 1; i <= n; i++) cin >> a[i];int hh = 0, tt = -1;for (int i = 1; i <= n; i++){while (hh <= tt && i - k >= q[hh]) hh++;//处理队首while (hh <= tt && a[q[tt]] <= a[i]) tt--;//处理队尾q[++tt] = i;//队尾元素加入if (i >= k) cout << a[q[hh]] << " ";//输出队首元素}cout << endl;return 0;
}

复杂度分析:

每个元素最多入队和出队一次,时间复杂度为O(N),队列最多存储k个元素,时间复杂度为O(K)。 


使用单调队列优化动态规划

在动态规划中,当状态转移需要在一个窗口查找极值时,可以使用单调队列优化时间复杂度。

题目链接:P5858 「SWTR-3」Golden Sword - 洛谷

状态表示:dp[i][j]表示加入第i个材料时, 锅内的材料个数为j(包括此时加入的),此时的耐久度的最大值。

状态转移方程的分析:加入第i个材料后的最大耐久度,等于加入第i-1个材料时最大的耐久度,再加上自身的耐久度,也就是再加上a[i]*j。

状态转移方程:dp[i][j]=max(dp[i][j],dp[i-1][k]+j*a[i]),j-1<=k<=min(w,j-1+s)。

正常使用动态规划:

第一层循环遍历i。

第二层循环遍历j,填写dp[i][j]。

但是由于有求[j-1,min(w,j-1+s)]最大值的操作,所以还需一层循环求最大值。

共三层循环,时间复杂度太大,需要进行优化。

#include <iostream>
using namespace std;
long long n, m, s;
long long a[5505];
long long dp[5505][5505];int main()
{cin >> n >> w >> s;for (long long i = 1; i <= n; i++)cin >> a[i];for (long long i = 0; i <= n; i++)for (long long j = 0; j <= w; j++)dp[i][j] = -1e18;dp[0][0] = 0;//dp[i][j]选第i个材料时,此时正好j个材料的最大耐久度for (long long i = 1; i <= n; i++)for (long long j = 1; j <= w; j++)for (long long k = j - 1; k <= min(w, s + j - 1); k++)dp[i][j] = max(dp[i][j], dp[i-1][k] + a[i] * j);long long ans = -1e18;for (int i = 0; i <= w; i++)ans = max(ans, dp[n][i]);cout << ans << endl;return 0;
}

使用单调队列优化后

在第三层的遍历,求区间[j-1,min(w,j-1+s)]的最大值,可以使用单调队列优化,降低时间复杂度。

//单调队列优化
#include <iostream>
#include <deque>
using namespace std;
long long n, w, s;
long long a[5505];
long long dp[5505][5505];int main()
{cin >> n >> w >> s;for (long long i = 1; i <= n; i++)cin >> a[i];for (long long i = 0; i <= n; i++)for (long long j = 0; j <= w; j++)dp[i][j] = -1e18;dp[0][0] = 0;//dp[i][j]选第i个材料时,此时正好j个材料的最大耐久度for (long long i = 1; i <= n; i++){deque<long long> q;  //单调队列,记录区间最大值  存储索引q.push_back(w);      //下面循环中w不会进队列,需提前进队列//[j-1,min(j-1+s,w)]//从右向左遍历,因为左端点固定,而右端点使用了minfor (long long j = w; j >= 1; j--){//[j-1,min(j-1+s,w)]while (!q.empty() && q.front() > min(w, s + j - 1))q.pop_front();while (!q.empty() && dp[i - 1][q.back()] < dp[i - 1][j-1])q.pop_back();q.push_back(j-1); //比较的是q.back()和j-1位置//统计结果dp[i][j] = a[i] * j + dp[i - 1][q.front()];}}long long ans = -1e18;for (int i = 0; i <= w; i++)ans = max(ans, dp[n][i]);cout << ans << endl;return 0;
}

例题 

题目链接:1438. 绝对差不超过限制的最长连续子数组 - 力扣(LeetCode) 

 使用两个单调队列来维护窗口的最大值和最小值,结合递增队列和递减队列。当最大值减最小值超出给定的limit时,窗口的左边界右移动,直到找到符合的位置,统计结果。

class Solution {
public:int longestSubarray(vector<int>& nums, int limit) {//单调队列,维护当前窗口的最大值和最小值deque<int> queMax,queMin;int n=nums.size();int left=0,right=0,ret=0;while(right<n){//维护队列的单调性//递减while(!queMax.empty()&&queMax.back()<nums[right])queMax.pop_back();//递增while(!queMin.empty()&&queMin.back()>nums[right])queMin.pop_back();//入队列元素queMin.push_back(nums[right]);queMax.push_back(nums[right]);while(!queMin.empty()&&!queMax.empty()&&queMax.front()-queMin.front()>limit){if(nums[left]==queMin.front())queMin.pop_front();if(nums[left]==queMax.front())queMax.pop_front();left++;}ret=max(ret,right-left+1);right++;}return ret;}
};

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

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

相关文章

矩阵的扩展运算(MATLAB和pytorch实例)

秩&#xff08;Rank&#xff09;的定义 秩的计算 初等行变换法&#xff08;最常用&#xff09;行列式法&#xff08;仅适用于方阵&#xff09; 满秩的分类方阵的满秩非方阵的满秩几何意义应用场景判断方法 矩阵的特征值 定义求解特征值 特征方程步骤 关键性质 迹与行列式相似矩…

python面试题整理

Python 如何处理异常&#xff1f; Python中&#xff0c;使用try 和 except 关键字来捕获和处理异常 try 块中放置可能会引发异常的代码&#xff0c;然后在except块中处理这些异常。 能补充一下finally的作用吗&#xff1f; finally 块中的代码无论是否发生异常都会执行&#xf…

linux之perf(17)PMU事件采集脚本

Linux之perf(17)PMU事件采集脚本 Author: Once Day Date: 2025年2月22日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Perf性能分析_Once_day的博…

Java数据结构-排序

目录 一.本文关注焦点 二.七大排序分析及相关实现 1.冒泡排序 2.简单选择排序 3.直接插入排序 4.希尔排序 5.堆排序 ​编辑 6.归并排序 7.快速排序 一.本文关注焦点 各种排序的代码实现及各自的时间空间复杂度分析及稳定性。 时间复杂度&#xff1a;在比较排序中主…

改进收敛因子和比例权重的灰狼优化算法【期刊论文完美复现】(Matlab代码实现)

2 灰狼优化算法 2.1 基本灰狼优化算法 灰狼优化算法是一种模拟灰狼捕猎自然群体行为的社会启发式优化算法&#xff0c;属于一种新型的群体智能优化算法。灰狼优化算法具有高度的灵活性&#xff0c;是当前较为流行的优化算法之一。灰狼优化算法主要分为三个阶段&#xff1a;追…

创建Linux虚拟环境并远程连接

目录 下载VMware软件 下载CentOS 创建虚拟环境 远程连接Linux系统 下载VMware软件 不会的可以参考 传送门 下载CentOS 不会的可以参考 传送门 创建虚拟环境 打开VMware软件&#xff0c;创建虚拟机 选择典型安装 找到我们安装好的centOS文件&#xff0c;之后会自动检…

汽车智能制造企业数字化转型SAP解决方案总结

一、项目实施概述 项目阶段划分&#xff1a; 蓝图设计阶段主数据管理方案各模块蓝图设计方案下一阶段工作计划 关键里程碑&#xff1a; 2022年6月6日&#xff1a;项目启动会2022年12月1日&#xff1a;系统上线 二、总体目标 通过SAP实施&#xff0c;构建研产供销协同、业财一…

《Head First设计模式》读书笔记 —— 命令模式

文章目录 本节用例餐厅类比点餐流程角色与职责从餐厅到命令模式 命令模式第一个命令对象实现命令接口实现一个命令 使用命令对象NoCommand与空对象 定义命令模式支持撤销功能使用状态实现撤销多层次撤销 One One One …… more things宏命令使用宏命令 队列请求日志请求 总结 《…

基于YOLO11深度学习的运动鞋品牌检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

DAY08 List接口、Collections接口、Set接口

学习目标 能够说出List集合特点1.有序2.允许存储重复的元素3.有带索引的方法(练习 add,remove,set,get) 能够使用集合工具类Collections类:static void sort(List<T> list) 根据元素的自然顺序 对指定列表按升序进行排序。static <T> void sort(List<T> lis…

shell编程总结

前言 shell编程学习总结&#xff0c;1万3千多字带你学习shell编程 往期推荐 14wpoc&#xff0c;nuclei全家桶&#xff1a;nuclei模版管理工具Nuclei 哥斯拉二开&#xff0c;免杀绕过规避流量检测设备 fscan全家桶&#xff1a;FscanPlus&#xff0c;fs&#xff0c;fscan适用…

OpenAI ChatGPT在心理治疗领域展现超凡同理心,通过图灵测试挑战人类专家

近期&#xff0c;一项关于OpenAI ChatGPT在心理治疗领域的研究更是引起了广泛关注。据报道&#xff0c;ChatGPT已经成功通过了治疗师领域的图灵测试&#xff0c;其表现甚至在某些方面超越了人类治疗师&#xff0c;尤其是在展现同理心方面&#xff0c;这一发现无疑为AI在心理健康…

【智能客服】ChatGPT大模型话术优化落地方案

本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 一、项目背景 1.1 行业背景 1.2 业务现…

【JavaWeb12】数据交换与异步请求:JSON与Ajax的绝妙搭配是否塑造了Web的交互革命?

文章目录 &#x1f30d;一. 数据交换--JSON❄️1. JSON介绍❄️2. JSON 快速入门❄️3. JSON 对象和字符串对象转换❄️4. JSON 在 java 中使用❄️5. 代码演示 &#x1f30d;二. 异步请求--Ajax❄️1. 基本介绍❄️2. JavaScript 原生 Ajax 请求❄️3. JQuery 的 Ajax 请求 &a…

[Android]APP自启动

APP添加自启动权限&#xff0c;重启设备后自动打开APP。 1.AndroidManifest.xml <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.an…

Moonshot AI 新突破:MoBA 为大语言模型长文本处理提效论文速读

前言 在自然语言处理领域&#xff0c;随着大语言模型&#xff08;LLMs&#xff09;不断拓展其阅读、理解和生成文本的能力&#xff0c;如何高效处理长文本成为一项关键挑战。近日&#xff0c;Moonshot AI Research 联合清华大学、浙江大学的研究人员提出了一种创新方法 —— 混…

cs224w课程学习笔记-第2课

cs224w课程学习笔记-第2课 传统图学习 前言一、节点任务1、任务背景2、特征节点度3、特征节点中心性3.1 特征向量中心性&#xff08;Eigenvector Centrality&#xff09;3.2 中介中心性&#xff08;Betweenness Centrality&#xff09;3.3 接近中心性&#xff08;Closeness Cen…

Centos虚拟机扩展磁盘空间

Centos虚拟机扩展磁盘空间 扩展前后效果1 虚拟机vmware关机后&#xff0c;编辑2 扩展2.1 查看2.2 新建分区2.3 格式化新建分区ext42.3.1 格式化2.3.2 创建2.3.3 修改2.3.4 查看 2.4 扩容2.4.1 扩容2.4.1 查看 扩展前后效果 df -h1 虚拟机vmware关机后&#xff0c;编辑 2 扩展 …

1.13作业

1 if(!preg_match("/[0-9]|\~|\|\|\#|\\$|\%|\^|\&|\*|\&#xff08;|\&#xff09;|\-|\|\|\{|\[|\]|\}|\:|\|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){eval($c); 构造数组rce ?ceval(array_pop(next(get_defined_vars()))); post传参:asystem("c…

如何在 SpringBoot 项目使用 Redis 的 Pipeline 功能

本文是博主在批量存储聊天中用户状态和登陆信息到 Redis 缓存中时&#xff0c;使用到了 Pipeline 功能&#xff0c;并对此做出了整理。 一、Redis Pipeline 是什么 Redis 的 Pipeline 功能可以显著提升 Redis 操作的性能&#xff0c;性能提升的原因在于可以批量执行命令。当我…