【LeetCode刷题笔记-55 239:滑动窗口最大值】

题目:
在这里插入图片描述
害,今天这题用自己的方法超时了,我自己的方法是暴力算法的优化版。即不是每次都去找最大值,而是暂存这个最大值和它的序号,然后再分别处理。将测试用例里面最大的那个单独处理之后,我自己的方法其实是能过的。

官方题解我看过之后,中间的双端队列感觉就是我这个方法的再次优化版本。所以我这里重点介绍这个方法

思路:
题目还是很好理解的,只是我们要考虑怎么让这个找寻的过程更快。

1.我们可以注意到,在窗口滑动的过程中,最大值很多时候是不变的。
所以就可以想到记录这个最大值和它对应的序号。
这样,就可以大幅度的节约每一次都去区间里寻找最大值的时间。然后,当它超过左边界或者是有更大的元素值时再进行处理。

2.答案则在这个想法上使用了双端队列进一步优化。对于一个队列,队列永远保持单调递减。然后,对于每一个右窗口遍历到的元素值,去比较,然后确定放置在队列的哪个位置。(小于右窗口值的元素可以永远弹出了,因为这时插入的右窗口值会后出队

操作顺序是这样:
(1)首先,对于右窗口进来的元素n,与队列中的元素比较,不断弹出队列元素,直到遇到大于当前元素n的值停止。即保持队列的单调递减。
(2)然后,再判断左窗口是否越过了当前区间最大值的序号(队首序号)。如果越过,则弹出队首元素,让第二大的元素继承队首元素。

3.为了操作方便,题解存储的是序号而不是值,因为序号是需要做判断的,而值都可以由序号去检索,是一个一对一映射的关系。如果存储的是值,判断左窗口越过最大值序号这一步就会很困难。因为值不能一对一映射回序号。

C++代码(附带测试,还包括了我自己的算法,大家可以自行比较异同)

#include<iostream>
#include<vector>
#include<algorithm>
#include <deque>
using namespace std;

//行得通但是会超时 
//class Solution {
//public:
//    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
//		int i = 0;//左边窗口边界 
//		int j = k-1;//右边窗口边界
//		int max = nums[0];//区间最大值
//		int maxindex = 0;
//		vector<int> ans;//答案数组 
//		for(int l = 0;l<k;l++)//初始化 
//		{
//			if(nums[l]>max)
//			{
//				max = nums[l];
//				maxindex = l;
//			}
//		}
//		
//		while(j<nums.size())
//		{
//			if(i==j)//窗口值为1,在这个算法里窗口值为1是特殊情况 
//			{
//				maxindex = j;
//				max = nums[j];
//			}
//			
//			//通常情况 
//			if(i==maxindex+1)//弹出首元素了 
//			{
//				maxindex = findmax(nums,i,j);//从i,j区间中找出下一个最大值标记号
//				max = nums[maxindex];
//			}
//			if(nums[j]>max)
//			{
//				//如果右窗口进来的值大于当前最大值 
//				maxindex = j;
//				max = nums[j];
//			}
//			ans.push_back(max);
//			i++;
//			j++;
//		}
//		return ans;
//    }
//    //寻找最大值标记函数 
//    int findmax(vector<int>& nums,int i,int j)
//    {
//    	int max = nums[i];
//    	int maxindex = i;
//    	for(int l=i;l<j;l++)
//    	{
//    		if(nums[l]>max)
//    		{
//    			max = nums[l];
//    			maxindex = l;
//			}
//		}
//		return maxindex;//返回最大值徐好 
//	}
//};


//题解算法
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    	//注意该算法队列q存储的是元素的序号而不是具体值 
        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;
    }
};

int main()
{
	vector<int> nums = {1,3,-1,-3,5,3,6,7};
	int k = 3;
	Solution s;
	vector<int> ans = s.maxSlidingWindow(nums,k);
	for(auto& a:ans)
	{
		cout<<a<<endl;
	}
	
	 
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值