题目描述(引自剑指offer)
给定一个整型数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,它们的最大值分别为{4,4,6,6,6,5},如下表所示。
数组中的滑动窗口 | 滑动窗口中的最大值 |
[2,3,4],2,6,2,5,1 | 4 |
2,[3,4,2],6,2,5,1 | 4 |
2,3,[4,2,6],2,5,1 | 6 |
2,3,4,[2,6,2],5,1 | 6 |
2,3,4,2,[6,2,5],1 | 6 |
2,3,4,2,6,[2,5,1] | 5 |
菜鸡与大佬的对话
菜鸡修炼坊
题目分析
经过在菜鸡修炼坊的修炼,菜鸡对线性表有了一个整体的把握。于是,菜鸡鼓起勇气向题目发起挑战。
掌握了线性表的菜鸡已经自动忽略了题目的暴力解法。一番分析之后,菜鸡发现,一个滑动窗口可以看作一个队列。窗口滑动的过程对应着队首元素出队和新元素入队的过程。而问题的关键就在于如何从队列中找到最大值。菜鸡很快想到可以利用辅助栈来记录队列每时每刻的最大值。忽然间,菜鸡脑中灵光一闪,是否可以定义一种数据结构,可以同时满足队列和栈的特性。查阅资料之后,菜鸡震惊地发现确实有这样一种数据结构,名为双端队列。
菜鸡开始明白大佬所说的“修炼要融会贯通”是什么意思,也渐渐体会到思想的重要性。菜鸡内心波澜四起,那种冲击不是成功解答几道题目可比的。菜鸡意识到,这可能才是修炼的意义所在。思考过后,菜鸡决定用Java代码把自己的心路历程描绘出来。
代码实现
import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
public ArrayList<Integer> maxInWindows(int[] num, int size) {
ArrayList<Integer> result = new ArrayList<>();
if (num == null || num.length == 0 || size < 1 || size > num.length) {
return result;
}
// 申请一个LinkedList用作双端队列
// 双端队列可以看作同时拥有栈和队列特性的数据结构
LinkedList<Integer> queue = new LinkedList<>();
for (int i = 0; i < num.length; i++) {
// 出栈
while (!queue.isEmpty() && num[queue.peekLast()] < num[i]) {
queue.pollLast();
}
// 入队(入栈)
queue.offerLast(i);
// 出队
if (queue.peekFirst() == i - size) {
queue.pollFirst();
}
// 记录结果
if (i >= size - 1) {
result.add(num[queue.peekFirst()]);
}
}
return result;
}
}
写罢之后,菜鸡发现只要遍历一次数组即可得到结果,因此代码的时间复杂度为o(n)。此次修炼之后,菜鸡对之前几次修炼的内容也进行了一次总结回顾,夯实基本功,期待新的突破。
相关链接