数据结构录 之 单调队列单调栈。
隊列和棧是很常見的應用,大部分算法中都能見到他們的影子。
而單純的隊列和棧經常不能滿足需求,所以需要一些很神奇的隊列和棧的擴展。
其中最出名的應該是優先隊列吧我覺得,然后還有兩種比較小眾的擴展就是單調隊列和單調棧。
?
先來看一個問題,給一個長度為N的數列,a1,a2。。。aN,然后給一個k<=N,求輸出b1,b2。。。bN這N個數,其中 bi=max( aj | j<=i && j>i-k && j>0 )。
比較樸素的想法是用一個Nk復雜度的循環來求,但是這樣的話如果N很大的話就太慢了。
然后還有一種想法是維護一個BST,然后for循環從左到右,依次加入到BST里面,如果某個數超出了k的范圍,就從BST中刪除。
偽代碼如下:
1 void getans() { 2 BST tree; 3 4 for(int i=1,j=1;i<=N;++i) { 5 tree.insert(a[i]); 6 while(j<=i-k) { 7 tree.erase(a[j]); 8 --j; 9 } 10 cout<<tree.max()<<endl; 11 } 12 }這樣的話因為每個數只insert一次,最多erase一次,所以復雜度是NlogN的,已經很不錯了。
?
但是BST比較高級,所以速度并不快,那么能不能根據這個問題的特點來設計一種更快的數據結構來解決?
先看這個問題,如果for循環從左到右來求b的話,就像是有個長度為k的框框一次次向右移動,每次求框內的最大值。
如果類比到隊列的話,就是for循環的時候每次push一個數在隊尾,然后把最前面那個超出的數pop出來,然后求隊列內的最大值就行了。
但是一般的隊列并不能求最大值,就需要一些擴展型的隊列了。
?
單調隊列就是隊列內所有數都是單調遞增的或者遞減的。下面按照從隊首到隊尾遞減的隊列來討論。
先看看push(x):
如果當前隊列為空的話,直接push進去就行。
如果當前隊列末尾的數比x大,那么直接放到隊尾,這時仍然是單調的。
如果末尾的數比x小的話,就扔掉隊尾的數,然后再重復上面的步驟push(x)。
比如隊列中是 ?5 4 2 1,然后push 3 進去的話,就把1和2扔掉,變成5 4 3,如果再push 7 進去的話,就把5 4 3 扔掉,隊列變成了 7 。
然后pop的話和一般隊列沒有區別。
?
然后這個數據結構如果應用到這個問題上的話,看看答案是否是對的。
for循環從左到右,然后每次push當前的ai,然后判斷如果隊首的元素的位置超出了框框,就pop出來扔掉。然后這是bi就等于pop完之后隊首的數。
1 struct Queue { 2 int val[MaxN],pos[MaxN]; 3 int first,last; 4 5 void init() { 6 first=last=0; 7 } 8 9 void push(int v,int p) { 10 while(last-first>0 && val[last-1]<=v) --last; 11 val[last]=v; 12 pos[last++]=p; 13 } 14 15 void pop() { 16 if(last-first>0) ++first; 17 } 18 19 int firstPos() { 20 return pos[first]; 21 } 22 23 int firstVal() { 24 return val[first]; 25 } 26 }; 27 28 void getans() { 29 Queue que; 30 que.init(); 31 32 for(int i=1;i<=N;++i) { 33 que.push(a[i],i); 34 while(que.firstPos()<=i-k) que.pop(); 35 cout<<que.firstVal().val<<endl; 36 } 37 }?
先來看看這樣對不對,首先隊列是單調的,所以隊首的數一定是最大的,這個數在失效的時候,在他位置前面的所有數也一定都失效了,而他位置后面的所有數還沒失效,仍然符合最大的前面,也就是最大的仍然還在隊列中沒有被扔掉。所以下一次詢問的時候仍然答案是對的。
?
然后看看復雜度如何,每個數只push了一次,然后最多會被扔掉一次,所以雖然push里面有while循環,但是這N個數每個最多被遍歷一次然后就被扔掉了,所以for循環N次下來,均攤的復雜度是O(1)的對于每個push和pop操作,所以總復雜度是O(N)的。
?
?
然后這就是單調棧,單調棧和單調隊列區別不大,都是每次push的時候要維護單調性。
有一道題目 POJ 2796?,需要先進行轉化然后在使用單調棧來解決。
?
單調棧和單調隊列在大部分情況下是一種工具,對于一些問題能夠優化到N的復雜度,這樣會比logN快很多。所以其實有些情況下不用這個,用其他的數據結構也是可以做的。
轉載于:https://www.cnblogs.com/whywhy/p/5066306.html
總結
以上是生活随笔為你收集整理的数据结构录 之 单调队列单调栈。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WEBApp-搭建Android开发环境
- 下一篇: ajax跨域请求问题