面試題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;
}