单调队列的使用

单调队列其实就是一个队列,只是使用了一点巧妙的方法使得队列中的元素全都是单调递增(或单调递减)的

单挑队列主要解决以下问题:

滑动窗口在滑动时,r++代表右侧数字进入串口,l++代表左侧数字出窗口

这个过程中,想随时得到当前滑动窗口的最大值或最小值

 滑动窗口最大值

具体操作

首先我们先讨论滑动窗口加入数字后,单调队列的情况(也就是r ++ )

假设这个数组为[6,5,4,3,5,6]

注意我们这个单调队列里只放数组元素的下标,并且里面的下标对应的数值大小是从大到小的

因此我们先放入下标0(也就是a[0]),然后再放入下标1,再放入下标2,再放入下标3

此时碰到了下标4(也就是a[4] = 5)

因为我们是必须严格按照从大到小

所以弹出下标1,2,3

再放入下标4

然后碰到了下标5(也就是a[5] = 6)

弹出下标4,0

放入下标5

那滑动窗口中减少数字后,单调队列的操作又该怎么办呢?

我们假设数组为[6,5,4,3,5,6]

假设此时单调队列为

当窗口减少数字(也就是 l ++)

我们就要判断单调队列的头元素是否为过期下标(就是头元素小于 l )

如果是过期下标就把它从头部弹出

解释:

关键点:维持可能性

假设一个队列

我们先放入下标0( a[0] )

如果此时就要求滑动窗口的最大值,那它就是单调队列的头元素

然后我们再放入下标1( a[1] )

为什么要放在下标 0 ( a[0] )的右边?

因此此时滑动窗口的最大值仍然是下标0( a[0] )

但是如果此时让下标 0 ( a[0] )弹出的话(也就是 l ++),此时下标 1 ( a[1] )就是头元素(也就是最大值)

然后我们再放入下标2( a[2] ),它自然也就是放在了下标 1 ( a[1] )的右边(它也有可能会成为最大值)

注意:我们单调队列的存储的都是下标

此时要放入下标4( a[4] )

那我们为什么要弹出1,2,3?

因为它们再也没有机会变成最大值了(因为4位置一定会比1,2,3位置要晚过期,并且4位置的值比1,2,3位置都要大)

 那为什么不弹出6?

因此此时滑动窗口的最大值是下标0( a[0] )。

当 l ++时

把 0 从头部弹出

整体的复杂度为o(n)

解题思路:

1.我们先生成 k - 1 的窗口,把它放入单调队列中

2.放入一个元素(r++),放入单调队列中(此时窗口长度为k)

3. l ++ ,考虑单调队列中是否有过期下标(此时窗口长度为k-1)

不断重复2,3

代码:

# include <stdio.h>int main()
{int k;// 滑动窗口大小 int n; //数组长度 scanf("%d %d", &k, &n)int num[n];for (int i=0; i<n; ++i)scanf("%d", &num[i]);int deque[n] //单调队列int h,t; // 用于更新单调队列 h表示头元素的下标,t表示下一个元素该放的位置//当h == t 时表示单调队列里没有元素h = 0;t = 0;	 //先形成k-1的窗口 for (int i=0; i<k-1; ++i){while (h<t && num[deque[t-1]] <= num[i])t = t - 1; //直接将deque的元素进行覆盖 deque[t] = i;t++;}int m = n - k + 1; //进行的次数//当前窗口为k-1长度for (int l=0, r=k-1; l<m; ++l, ++r){//少一个,要让r位置进去while (h<t && num[deque[t-1] <= num[r]])t = t - 1;deque[t] = r;t = t + 1;//让l位置的数出去if (deque[h] == l)h = h + 1;} 
}

算法讲解054【必备】单调队列-上_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV11h4y1w7Bu/?vd_source=617dcf7ed08ed9625bdfcadc93c4fac7

拓展

除了单调队列最经典的用法之外,在很多问题里单调队列还可以维持求解答案的可能性

1.单调队列里的所有对象按照规定好的单调性来组织

2.当某个对象从队尾进入单调队列时,会从队头或者队尾依次淘汰单调队列里,对后续求解答案没有帮助的对象

3.每个对象一旦从单调队列弹出,可以结算此时这个对象参与的答案,随后这个对象不再参与后续求解答案的过程

4.其实是先有对题目的分析!进而发现单调性,然后利用单调队列的特征去实现

题目:

(一)

假设arr[1,1,1,6,8,2,9,1,5,4,1]

思路:

当我们遍历到 i 下标时,如果子数组必须以 i 为右边界,那么要往左延伸最短的距离能使累加和大于等于k

我们求出一部分的前缀和为

我们必须要找到某一段的前缀和大于等于k,然后再以它右边界的数字为结尾的情况下,向左延伸最短的距离能使累加和大于等于k

例如:

当k = 10时

0~4,它们的前缀和是17,所以我们要找到前面的某段的前缀和小于等于7,且足够靠右,把这段删去,这样就能得到某一段的前缀和大于等于k,并且向左延伸最短的距离能使累加和大于等于k。

0~2的前缀和是3,并且是足够靠右的,所以删去后,得到3~4的前缀和为14,长度为2,满足条件

因此我们可以得到统一的规律:

我们首先要找到以 i 为结尾的前缀和为 x ,并且 x >= k,

要想找到一段的前缀和大于等于k,并且长度最短

必须要删去一段前缀和小于等于 x - k,并且足够靠右的

我们建立一个单调队列

存储的是前缀和的下标,下标对应的数值大小是从小到大的

假设arr[1,1,1,6,8,2,9,1,5,4,1]

前缀和为:

k 为 10

下一个为就是4位置,此时0~4的前缀和为17,大于10,开始判断所有的可能

先判断队头

0~0的前缀和为1,那么1~4的和就是16,符合条件,所以ans = 4

然后把 0 从头部弹出(没有这种0~0的可能性了,因为我们判断出来1~4是符合条件的,我们要求最短的达标子数组,后面的数组肯定会从 1~4 开始往后计算,不会从0开始。换句话说,即使后面的子数组用上了0~0,那它的长度肯定不如是 1~4 短)

继续进行判断队头

0~1的前缀和为2,那么2~4的和就是15,符合条件,所以ans = 3

然后把1从头部弹出

继续进行判断队头

0~2的前缀和为3,那么3~4的和就是14,符合条件,所以ans = 2

然后把2从头部弹出

继续进行判断队头

0~3的前缀和为7,那么4~4的和就是8,不符合条件

然后把 4 从队尾放入单调队列

然后不断进行判断……

综上:

在单调队列中,不断放入前缀和的下标,如果遇到下标对应的前缀和大于等于k,则进行判断

如果我们遇到了负数呢:

看以下的情况:

以上为原数组

假设k = 100

此时我们依次放入前缀和的下标

下一个该放入的前缀和下标为3(0~3的前缀和是6)

那么我们就要把0,1,2全部从队尾弹出

(这里就解释了为什么要从小到大进行排列,和为什么要进行弹出操作:1.如果再继续进行下去,如碰到了一个前缀和为 x ,此时x >= k,如果此时x - 6都不符合条件(也就是x - 6 < k),那么x - 14一定是不符合条件的,所以必须要从小到大进行排列。2.因为后加入的数字,与对应的符合条件的 i 点距离肯定是更近的,所以可以把大于等于它的数字都弹出。)

然后和上面相同,不断的进行判断

代码:

# include <stdio.h>int main()
{int kint a[11];scanf("%d", &k);for (int i=1; i<=10; ++i)scanf("%d", &a[i]);int deque[10];int h, t = 0;int md[11];md[0] = 0;md[1] = a[1];for (int i=2; i<=10; ++i)md[i] = md[i-1] + a[i];int ans = 999;int k = 1;for (int i=1; i<=10; ++i){while(h !=t && md[i]-md[deque[h] >=k){ans = min(ans, i - deque[h]);h = h + 1;}while (h < t || md[deque[t-1]]> md[i])t = t - 1;deque[t] = i;t = t + 1;}}
}

(二)

因为 xi 始终大于 xj

所以我们可以直接把绝对值拆掉

所以题目就变为了求 yi + yj + xj - xi = yj + xj + yi - xi;

因此我们可以建立一个滑动窗口的单调队列

里面的元素的是yi - xi的值的下标,从大到小排列

代码:

# include <stdio.h>int main()
{//存储 i号点的 x,y int points[100][2];for (int i=0; i<100; ++i)scanf("%d %d", &points[i][0], &points[i][1]);int deque[100][2];int h, t = 0;int k;int ans = 0;for (int i=0; i<100; ++i){int x = points[i][0];int y = points[i][1];while (h < t && deque[h][0] + k < x)h = h + 1;if (h < t){ans = max(ans, deque[h][1] - deque[h][0] + x + y);}while (h < t && deque[t-1][1] - deque[t-1][0] <= y - x)t = t - 1;}deque[t][0] = x;deque[t][1] = y;t = t + 1;}

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

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

相关文章

力扣hot100:15.三数之和(双指针/哈希表)

分析&#xff1a; 三数和问题&#xff0c;这里和两数之和不一样&#xff0c;返回的是值&#xff0c;因此可以对其进行排序&#xff0c;使用双指针。 一、一层循环双指针 class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {sort…

O2OA(翱途)通过服务来调用接口实现单点登录案例

本文介绍O2OA服务管理中&#xff0c;接口的权限设定和调用方式。 创建接口 具有服务管理设计权限的用户&#xff08;具有ServiceManager角色或Manager角色&#xff09;打开“服务管理平台”&#xff0c;进入接口配置视图&#xff0c;点击左上角的新建按钮&#xff0c;可创建一…

【计算机考研】408+数学全年备考规划

先回答题主问题&#xff0c;不看学校规定的教材是完全OK的&#xff0c;我就是全程跟着王道下来&#xff0c;高分上岸了。 计算机专业知识除非自己大一大二学完以后定期复习&#xff0c;其实大家到了该准备考研的时候都已经忘得差不多了&#xff0c;所以不用担心&#xff0c;就…

根据用户名称实现单点登录

一、参数格式 二、后端实现 Controller层 public class IAccessTokenLoginController extends BaseController {Autowiredprivate ISysUserService sysUserService;Autowiredprivate ISingleTokenServiceImpl tokenService;/*** 登录方法** return 结果*/PostMapping("/l…

神经网络推理优化方法总结

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

HTTP代理扫描的技术解析(HTTP代理扫描的技术原理和使用方法)

HTTP代理扫描的技术解析 近年来&#xff0c;随着互联网的快速发展&#xff0c;HTTP代理扫描技术也日益成熟。HTTP代理扫描是指通过扫描网络中的HTTP代理服务器&#xff0c;获得有效代理的IP地址和端口&#xff0c;进而实现网络请求的转发。通过HTTP代理扫描&#xff0c;用户可…

只会Vue的我,用两天学会了react,这个方法您也可以

公众号&#xff1a;需要以下pdf&#xff0c;关注下方 2023已经过完了&#xff0c;让我们来把今年的面试题统计号&#xff0c;来备战明年的金三银四&#xff01;所以&#xff0c;不管你是社招还是校招&#xff0c;下面这份前端面试工程师高频面试题&#xff0c;请收好。 背景 由…

游泳耳机哪种款式好?五大高分机型硬核推荐!

游泳&#xff0c;一项既能锻炼身体又能放松心情的运动。在水中的每一道波纹&#xff0c;每一次划手&#xff0c;都仿佛能带走我们生活中的疲惫与压力。而在这个过程中&#xff0c;音乐无疑是最棒的伴侣。一款好的游泳耳机&#xff0c;不仅能让我们在水中畅游的同时享受到高品质…

EUV光刻机,大结局?

前言 芯片号称现代社会的“工业粮食”&#xff0c;是信息产业的基石。自1958年集成电路诞生之日以来&#xff0c;芯片产业日益成为国民经济和社会发展的战略性、基础性、先导性产业。芯片深刻地改变了人类的生产生活方式&#xff0c;从手机、家电、汽车等以大众消费者为导向的…

Docker镜像仓库-Docker的私有镜像仓库的搭建

Docker镜像仓库 文章目录 Docker镜像仓库1.私有镜像仓库搭建1.1.简化版镜像仓库1.2.带有图形化界面版本1.3.配置Docker信任地址 2、私有仓库的拉取和推送 **镜像仓库&#xff08; Docker Registry &#xff09;**有公共的和私有的两种形式&#xff1a; 公共仓库&#xff1a;例如…

基于SpringBoot的网上订餐系统论文

摘 要 随着我国经济的飞速发展&#xff0c;人们的生活速度明显加快&#xff0c;在餐厅吃饭排队的情况到处可见&#xff0c;近年来由于新兴IT行业的空前发展&#xff0c;它与传统餐饮行业也进行了新旧的结合&#xff0c;很多餐饮商户开始通过网络建设订餐系统&#xff0c;通过专…

集团化日企统一平台管理,居然有那么多好处

你知道日本第一家在中国投资的企业是谁吗&#xff1f; 松下电器。 继松下之后&#xff0c;其他日本公司也开始陆续在中国投资发展。例如我们今天耳熟能详的丰田、电装、东芝、资生堂、永旺等等。 这些企业有一个共同的管理特点&#xff0c;即都采用集团化经营模式。随着在华…