239  滑动窗口的最大值

本文深入解析了使用单调队列求解滑动窗口最大值的经典算法,通过实例演示了算法步骤,对比了单调队列与单调栈的异同,并提供了两种不同实现方法的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                                                                                                                               点击此处返回总目录

 

 

【题目】

 

【方法一:单调队列】

 

单调队列知识:

我们这里用单调队列来解决。我们前面学过单调栈了。再来学单调队列就很简单。

 

比如为维护了一个从大到小的单调栈,元素是9,7,4。当新来一个元素5的时候,就跟队尾的元素比较一下,如果小于队尾元素,就直接入队。如果大于队尾元素,就先让队尾元素出队,直到遇到大的元素或者队为空。                       

                      

我们可以根据前面单调栈的代码,轻松写出如下代码:

 

for(int i = 0 ;i<n;i++){

    while(队不为空 && 队尾元素<nums[i]){

          队尾元素出队;

    }

    当前元素进队;

}

 

你发现了没有,如果把前面的出口堵上,这就是一个单调栈。

                                            

所以,单调队列跟单调栈差不多,如果不对队头进行操作的话。里面存放的是元素是,当前还没有在它右边找到更大值的那些元素。

也正是利用这一个性质,我们结合队列特有的出队操作,解决了当前窗口的最大值问题。后面详细讲。

 

说明:

因为需要队尾元素出来,所以这个单调队列不能是一般的队列,需要用双端队列来实现。

关于双端队列的基础知识,也很简单,可以看我的博客:https://blog.youkuaiyun.com/pengchengliu/article/details/91047320

 

 

题目分析:

上面说了,队列中维护的是当前还没有找到右边较大值的哪些结点。也就是从0到i的元素中,没有找到它右边比它更大的那些元素。

比如:

5 4 2 1 3

当i=3操作后,队内的元素为5,4,3。对应的下标是0,1,4。意味着从第0个数开始到第4个数结束没有比5更大的元素、从第1个数开始到第4个数结束没有比4更大的元素,从第4个数开始到第4个数结束没有比3更大的元素。

                          

而我们要求的是窗口(假设为3)内最大的数。也就是从2开始到4结束最大的数。

我们只需要将队列前面下标<2的元素移除,然后剩下的队头元素就是答案。

 

 

举例:

num[]    : 5  4  2  1  3  -1 -3  5  3          窗口大小k为3。

下  标    : 0  1  2  3  4   5  6  7  8  

ret[]下标:         0  1  2   3  4  5  6  

 

我们维护一个单调递减的队列。

(1)当i = 0时,5进队:

                         

(2)当i=1时,4比5要小,所以4进队:

                          

(3)当i=2时,2比4要小,所以2进队:

                           

         此时i刚好走完第一个窗口,所以ret[0]=队首元素=5。

 

(4)当i=3时,1比2小,1进队:

                           

         同时看一下队首元素有没有小于i-(k-1)。队首元素下标为0,小于2。所以把5移除。

                           

        此时,窗口的最大值为队首元素4。ret[1]=4。

 

(5)当i=4时,3大于1,1出队;3大于2,2出队。3小于4,3入队。

                             

        下标为4的时候,窗口为2,3,4。而队首下标为1<2,所以4移除。

                            

          当前最大值为ret[2]=3.

 

(6)当i=5时,-1入队。

                             

           队首下标没超。

           ret[3]=3.

 

(7)当i=6时,-3入队。

                              

           队首下标没超。

           ret[4]=3.

 

(8)当i=7时,5>-3,-3出队;5>-1,-1出队;5>3,3出队。空了,5入队。

                               

            队首下标没有超。

            ret[5]=5.

 

(9)当i=8时,3进队。

                               

             队首下标没超。

             ret[6]=5.

 

 

代码:

 

 

结果:

 

 

结果不算太好,但是这确实是本题的最经典的方法。

如果没有看懂的话,建议再看看剑指offer的解答(对应59题)。

 

 

 

【方法二】

其实也不需要这么复杂。一共就分2种情况:当max的值在最左边,当max的值不在最左边。

(1)max的值在最左边,也就是下一次移动的时候max将要被移除窗口外时。

如果max<i,那太好了,max就等于i就行了。

                                

如果max>i,那就必须要i与前面的k-1个数都比较一遍了。

                                   

(2)max不在最左边,意味着当i加入时,max不移除。

如果max>i,太好了,不动,max还是max。

                                      

如果max<i,那也不错。只需要将max更新为i即可。

                                         

写代码的时候,可以把第1个如果和第4个如果合并。

 

 

代码:

 

结果:

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值