#数据结构与算法学习笔记#剑指Offer62:滑动窗口的最大值 + 在线处理(Java、C/C++)

234 篇文章 1 订阅
80 篇文章 0 订阅

2019.3.3     《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门​​​​​​​

这道题也是一道效率题,字符串和数组一般可以考虑在线处理。下面对算法过程进行说明:

1.创建一个用于记录元素下标的双端队列list和最终结果序列result。

以下对于测试用例:输入数组{2,3,4,2,6,2,5,1}、滑动窗口大小3,进行算法过程说明。

2.对list进行初始化,从初始窗口倒数第二个元素k开始执行一次操作:从前往后将队列中元素k小的所有元素删除,并将元素k的位置加在队首(记住这次操作,接下来还会用到)。

初始窗口中的元素为{2,3,4},对k=3之前的所有元素进行操作,由于3最大,对应下标为1,最终list={1}。

3.从第一个窗口(第一个窗口最后一个元素)开始,执行上述操作。

第一个窗口中的元素为{2,3,4},对k=4之前的所有元素进行操作,由于4最大,对应下标为1,最终list={2}。

4.对list队尾的最大元素,检查是否超出窗口范围(队首-队尾+1?窗口大小,只需检查一次),若超出则删去队尾元素。将最大元素加入result。

当窗口进行到{3,4,2}的时候,list中的元素为{3,2}(队首为3,队尾最大为2),检查一下3-2+1<3,符合要求,将位置2对应元素4加入result。

当窗口进行到{2,5,1}的时候,list中的元素为{7,6,4}(队首为7,由于6(对应数字5)不比7(对应数字1)小,因此不删除,队尾最大为4(对应数字6)),检查一下队尾7-4+1>3,说明数字6在窗口范围外,删去。list最终为{7,6},将位置6对应元素5加入result。

5.循环上述操作3与检查4的过程,可以保证每次list队尾元素最大。


题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。


Java实现:

/**
 * 
 * @author ChopinXBP
 * 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
 * 例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5};
 * 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: 
 * {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, 
 * {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
 * 
 */

import java.util.ArrayList;
import java.util.LinkedList;

public class maxInWindows_63 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] num = {2,3,4,2,6,2,5,1};
		ArrayList<Integer> result = maxInWindows(num, 3);
		for(int i = 0; i < result.size(); i++) {
			System.out.print(result.get(i) + " ");
		}
	}

    public static ArrayList<Integer> maxInWindows(int [] num, int size)
    {
    	ArrayList<Integer> result = new ArrayList<>();
    	if(num == null || size <= 0 || size > num.length) return result;
    	//记录元素下标的队列 (只记录下标)	
    	LinkedList<Integer> list = new LinkedList<>();
    	//构建初始队列,从初始窗口倒数第二个元素k开始执行一次操作:从前往后将队列中元素k小的所有元素删除,并将元素k加在队首,此时队尾元素最大
    	for(int i = 0; i < size - 1; i++) {
    		while(!list.isEmpty() && num[i] > num[list.peekFirst()]) {
    			list.pollFirst();
    		}
    		list.addFirst(i);
    	}
    	
    	//初始队列最大元素为size - 1个,对与每一个新元素k = num[i]    	
    	for(int i = size - 1; i < num.length; i++) {
    		//从前往后移去比k小的元素,将k插入队首
    		while(!list.isEmpty() && num[i] > num[list.peekFirst()]) {
    			list.pollFirst();
    		}
    		list.addFirst(i);
    		//检查队尾元素(当前最大值的坐标)是否超出窗口范围(被移出窗口),若超出则删去(每次移动最多删去一个)
    		if(i - list.peekLast() + 1 > size) {
    			list.pollLast();
    		}
    		result.add(num[list.peekLast()]);
    	}
    	  	
    	return result;
    }	
}

C++实现示例:

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        vector<int> res;
        deque<int> s;
        for(unsigned int i=0;i<num.size();++i){
            //从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
            while(s.size() && num[s.back()]<=num[i])
                s.pop_back();

            //当当前窗口移出队首元素所在的位置,即队首元素坐标对应的num不在窗口中,需要弹出
            while(s.size() && i-s.front()+1>size)
                s.pop_front();

            //把每次滑动的num下标加入队列
            s.push_back(i);

            //当滑动窗口首地址i大于等于size时才开始写入窗口最大值
            if(size&&i+1>=size)
                res.push_back(num[s.front()]);
        }
        return res;
    }
};

#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值