题目:有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右滑一个位置。
例如,数组为[4,3,5,4,3,3,6,7],窗口大小为3时:
如果数组长度为n,窗口大小为w,则一共产生n-w+1个窗口的最大值。
请实现一个函数:
输入:整型数组arr,窗口大小为w。
输出:一个长度为n-w+1的数组res,res[i]表示每个窗口状态下的最大值。
以本题为例,结果应该返回{5,5,5,4,6,7}。
如果暴力求解时间复杂度为 O(N x w),这个结果显然不能让人满意,这里我们将介绍复杂度为 O(N)的解法。
本题的关键在于利于双端队列来实现窗口最大值的更新。首先生成双端队列 qmax,qmax中存放数组 arr 中的下标。
假设遍历 arr[i],qmax 的放入规则为:
1. 如果 qmax 为空,直接把下标 i 放入 qmax;
2. 如果qmax不为空,取出当前 qmax 队尾存放的下标,假设为 j,
a. 如果arr[j] > arr[i],直接把下标放入 qmax 的对尾
b. arr[j] <= arr[j],把 j 从 qmax 中弹出,继续 qmax 的放入规则
假设遍历到 arr[i],qmax 的弹出规则为:
如果 qmax 队头的下标等于 i-w,说明当前 qmax 队头的下标已经过期,弹出当前队头的下标即可。
根据上述放入和弹出规则,qmax便成了一个维护窗口为 w 的子数组的最大更新的结构。
下面举例说明给出的例子。
1 public static int[] getMaxWindow(int[] arr, int w) 2 { 3 if(arr == null || w < 1 || arr.length < w) 4 return null; 5 6 Deque<Integer> qmax = new LinkedList<Integer>(); 7 int[] res = new int[arr.length - w + 1]; 8 int index = 0; 9 for(int i = 0; i < arr.length; i++) 10 { 11 while(!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]) 12 { 13 qmax.pollLast(); 14 } 15 qmax.addLast(i); 16 if(i - w == qmax.peekFirst()) 17 qmax.pollFirst(); 18 if(i >= w - 1) 19 res[index++] = arr[qmax.peekFirst()]; 20 } 21 22 return res; 23 }
参考:程序员代码面试指南 IT名企算法与数据结构题目最优解,左程云