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

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

【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 ; };