問題描述
給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{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]}。
思路
這題是字節跳動測試開發實習生面試題原題。寫出來O(n2)的算法很容易。(方法一)
但是寫出來更優化的就難點了。 怎麼做? 我想到過用單調棧,但是單調棧不好使。因爲我們要維護的是局部最優。那咋整呢?我們可以設置一個max 和 maxIndex, max記錄最大值,maxIndex記錄最大值的下標。如果maxIndex在滑動窗口的範圍內,且新加入滑動窗口的元素不如max大,則直接加入max到res。 如果大於等於max,則更新max和maxIndex的值,再將max加入res。爲什麼等於max也要更新max和maxIndex的值? 因爲值相等,記錄靠右的,有利於我們維持這個最大值的統治範圍。(方法二)
方法一
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size){
ArrayList<Integer> res = new ArrayList<>();
if(num == null || num.length == 0 || size < 1 || num.length < size) return res;
for(int i = 0; i <= num.length-size; i++) res.add(getMax(num,i,i+size));
return res;
}
private int getMax(int[] num, int left, int right){
int max = Integer.MIN_VALUE;
for(;left<right;left++) max = Math.max(max,num[left]);
return max;
}
}
方法二
class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> res = new ArrayList<>();
if(num == null || num.length == 0 || size < 1 || num.length < size) return res;
initRes(res, num, size);
return res;
}
private void initRes(ArrayList<Integer> res, int[] num, int size){
int max = Integer.MIN_VALUE, maxIndex = -1;
for(int i = size-1; i < num.length; i++){
if(maxIndex < i-size+1){
// max不在滑動窗口內
max = num[i-size+1];
maxIndex = i-size+1;
for(int j = i-size+1; j <= i; j++){
if(max <= num[j]){
max = num[j];
maxIndex = j;
}
}
}else{
// max在滑動窗口內
if(num[i] >= max){ // 更新最大值
max = num[i];
maxIndex = i;
}
}
res.add(max);
}
}
}