面试题59:队列的最大值
题目描述
问题(1)滑动窗口的最大值
给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。
例如,如果输入数组{2, 3, 4, 2, 6, 2, 5, 1}及滑动窗口的大小3,那么一共存在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]}。它们的最大值分别为{4, 4, 6, 6, 6, 5}
问题(2)队列的最大值
请定义一个队列并实现函数max得到队列里的最大值,要求函数max、push_back和pop_front的时间复杂度都是O(1)。
问题分析
问题(1)分析
最直接的就是暴力法:在每个滑动窗口依次比较找出最大值,但是时间复杂度较高,答案不会让人满意。
可以考虑把每个可能成为最大值的数字记录下来,以便能快速的得到最大值。
我们建立一个两端开口的队列,放置所有可能是最大值的数字(存放的是对应的下标),且最大值位于队列开头。
从头开始扫描数组:
- 如果遇到的数字比队列中所有的数字都大,那么它就是最大值,其它数字不可能是最大值了,将队列中的所有数字清空,放入该数字,该数字位于队列头部;
- 如果遇到的数字比队列中的所有数字都小,那么它还有可能成为之后滑动窗口的最大值,放入队列的末尾;
- 如果遇到的数字比队列中最大值小,最小值大,那么将比它小数字不可能成为最大值了,删除较小的数字,放入该数字。
- 由于滑动窗口有大小,因此,队列头部的数字如果其下标离滑动窗口末尾的距离大于窗口大小,那么也删除队列头部的数字。
Java有内置的双端队列.ArrayDeque
问题(2)
上面的问题解决之后,这个问题就简单多了,同样利用一个双端队列来存储当前队列里的最大值以及之后可能的最大值。
在定义题目要求功能的队列时,除了定义一个队列data存储数值,还需额外用一个队列maxmium存储可能的最大值;此外,还要定义一个数据结构,用于存放数据以及当前的index值,用于删除操作时确定是否删除maxmium中最大值。
问题解答
问题(1)
public ArrayList<Integer> maxInWindows(int [] num, int size){
ArrayList<Integer> max = new ArrayList<Integer>();
if(num==null || num.length<=0 || size<=0 || size>num.length) {
return max;
}
ArrayDeque<Integer> indexDeque = new ArrayDeque<>();
for(int i=0;i<size-1;i++){
while(!indexDeque.isEmpty() && num[i]> num[indexDeque.getLast()]) {
indexDeque.removeLast();
}
indexDeque.addLast(i);
}
for(int i=size-1;i<num.length;i++){
while(!indexDeque.isEmpty() && num[i]> num[indexDeque.getLast()]) {
indexDeque.removeLast();
}
if(!indexDeque.isEmpty() && (i-indexDeque.getFirst())>=size) {
indexDeque.removeFirst();
}
indexDeque.addLast(i);
max.add(num[indexDeque.getFirst()]);
}
return max;
}
问题(2)
// 存储数据
private ArrayDeque<InternalData> data = new ArrayDeque<>();
// 最大数值
private ArrayDeque<InternalData> maximum = new ArrayDeque<>();
private class InternalData{
int number;
int index;
public InternalData(int number,int index) {
this.number=number;
this.index=index;
}
}
private int curIndex;
public void push_back(int number) {
InternalData curData = new InternalData(number,curIndex);
data.addLast(curData);
while(!maximum.isEmpty() && maximum.getLast().number < number) {
maximum.removeLast();
}
maximum.addLast(curData);
curIndex++;
}
public void pop_front() {
if(data.isEmpty()) {
System.out.println("队列为空,无法删除!");
return;
}
InternalData curData = data.removeFirst();
if(curData.index==maximum.getFirst().index) {
maximum.removeFirst();
}
}
public int max() {
if(maximum==null){
System.out.println("队列为空,无法删除!");
return 0;
}
return maximum.getFirst().number;
}