1. 程式人生 > >【LeetCode & 劍指offer刷題】棧與隊列題5:59 隊列(滑動窗口)的最大值

【LeetCode & 劍指offer刷題】棧與隊列題5:59 隊列(滑動窗口)的最大值

blog 自定義 max while weight ++ 使用 type index

【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...)

59 隊列(滑動窗口)的最大值

題目一:滑動窗口的最大值

給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{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(nk),O(1), k為滑動窗的長度 */ /* 方法二:用一個雙端隊列來存各階段的最大值 用O(1)時間得到滑動窗口的最大值,隊列最大長度為k O(n),O(k) 存索引,這樣可以判斷窗口位置,從而決定是否移除隊首元素 */ class Solution { public: vector<int> maxInWindows(const vector<int>& num, unsigned int size) { vector<int> result;
if(size > num.size() || size < 1) return result; deque<int> index; //雙端隊列 //隊列中存的元素中,隊首元素為當前窗口下的最大值,隊首外的元素為最大值之後的次大值(隊列中索引指向元素為從大到小,均為局部極大值點) for(unsigned int i = 0; i < num.size(); i++) { while(!index.empty() && num[i] >= num
[index.back
()]) index.pop_back(); //從隊尾依次彈出隊列中比當前num值小的元素,同時也能保證隊列首元素為當前窗口最大值下標 index.push_back(i); //插入當前索引值,因為可能為其他窗口下的最大值 if(!index.empty() && i - index.front()+1 > size) index.pop_front();//如果隊首索引所指向的元素已經不在窗口中了就彈出 if(i >= size-1)//當有完整的窗口覆蓋後才開始push最大值 result.push_back(num[index.front()]); } return result; } }; 題目二:隊列的最大值 請定義一個隊列並實現函數max得到隊列裏的最大值,要求函數max、push_back和pop_front的時間復雜度都是O(1)思路:
  • 同上一題相同,我們要尋找隊列的最大值,相當與將滑動窗口設置為整個隊列
  • 這裏需要使用兩個隊列,一個隊列用來保存入隊的數據一個隊列用來保存隊列的當前最大值
  • 同時需要註意出隊操作,數據隊列出隊的同時需要判斷其索引是否和當前最大值隊列首部索引相同,如果相同則同時也將最大值隊列頭部出隊。
比較:
  • min棧的實現:隊列中是尾部插入,首部刪除(滑動窗口是這種情況,故用隊列較好),而min棧較方便,在一端插入和刪除
  • 最大值隊列在push當前值之前,需將之前較小的值出隊處理(從隊尾開始判斷),隊首元素即為當前隊列的最大值
template<typename T> class QueueWithMax { public: QueueWithMax() :currentIndex(0) {} void push_back(T number) { //該函數無需考慮隊首索引所指向的元素已經不在窗口中了就彈出的情況,因為滑動窗口相當於整個隊列 while (!maximums.empty() && number >= maximums.back().number) //剔除最大值隊列中較當前值小的元素 maximums.pop_back(); //保證最大值隊列中存的是主極大與次極大 InternaData internaData = {number, currentIndex}; data.push_back(internaData); maximums.push_back(internaData); //push當前元素,有可能其為之後窗口的最大值 ++currentIndex; } void pop_front() { if (maximums.empty()) throw new exception("queue is empty."); if (maximums.front().index == data.front().index) //如果彈出的元素即為當前最大值,註意把最大值隊列隊首元素也出隊 maximums.pop_front(); data.pop_front(); } T max() const { if (maximums.empty()) throw new exception("queue is empty."); return maximums.front().number; //最大值隊列隊首即為當前窗口最大值 } private: struct InternaData //自定義數據結構(數+索引) { T number; int index; }; deque<InternaData> data; //存儲隊列數據 deque<InternaData> maximums; //窗口相當於整個隊列 int currentIndex; };

【LeetCode & 劍指offer刷題】棧與隊列題5:59 隊列(滑動窗口)的最大值