今天在做數據結構課后練習題的時候,發現有一道題挺有意思的,問題是這樣的:給定兩個序列,給出算法用來判斷第二個序列是否為以第一個序列為入棧順序的出棧序列。比如給出了入棧的序列 EASY,那么給出一個算法判斷ASYE是否為它的一個出棧序列。在還沒有做出這個題目之前我想到了另外的一個問題:如果給出一個入棧的序列,那么怎么求出所有可能的出棧序列呢?那么我們先來解決第二個問題。假如你組合數學學的還行的話,這個時候你可能會想到在組合數學中的一個有名的數--卡塔蘭數。(http://zh.wikipedia.org/wiki/卡塔蘭數)
上面就是它的常見公式,很多問題都可以通過計算這個公式直接得到你想要的結果。比如給你了一個這樣的題目:已知一個入棧序列為1,2,3,4,5,求所有可能的出棧序列總數有多少?簡單的一算就知道了這個就相當於要計算C5 的值了,也許你已經知道答案了。那么我想問:你知道怎么去求出它的所有可能的序列而不是僅僅要出它的總數呢?此時,這個公式將顯得無力。那么我們接下來分析一下該如何解決這個問題。當面對比較復雜抽象的問題的時候,我們總是可以通過列舉簡單的例子來發現解決問題的規律。為了簡單起見,我們列舉了1,2,3,4,5 這5個數作為入棧的序列,簡單分析一下它的過程,也許就知道程序該如何寫了,當數字1入棧的時候,此時的棧將面臨兩個選擇,出棧?繼續入棧。也就是說任一時刻,對都會面臨兩種選擇,出棧還是入棧?這時,如果你對遞歸程序有一定的認識的話,也就你對程序的結構已經有思路了。(為了方便利用棧的數據結構采用了C++)
我寫的程序大概是這樣子的:
#include <iostream> #include <stack> using namespace std; static int count; void outprint(stack<int> q){ while(q.size()!=0) { cout << q.top() << "-> "; q.pop(); } cout << endl; count++; return; } //q 存放入棧序列 //stk 用於模擬入棧過程 //output 用於存放可能的出棧序列 void allPopSeq(stack<int> q,stack<int> stk,stack<int> output){ if((q.size() == 0)&&(stk.size()==0)&&(output.size() == 5)) { outprint(output); return; } if(q.size()!=0){//入棧 int v = q.top(); stk.push(v); q.pop(); allPopSeq(q,stk,output); stk.pop(); q.push(v);//回溯恢復 } if(stk.size()!=0) //出棧 { int v = stk.top(); stk.pop(); output.push(v); allPopSeq(q,stk,output); output.pop(); stk.push(v);//回溯恢復 } return; } int main(int argc,char** argv){ int arr[5] = {1,2,3,4,5}; stack<int> stkValues; stack<int> stkOutput; stack<int> tmp; int i; for(i = 0;i!= 5;++i){ stkValues.push(arr[i]); } allPopSeq(stkValues,tmp,stkOutput); cout << count << endl; }
這樣當我們再回頭看一下最初的問題:判斷一個序列是否為某一個序列的出棧序列,這個問題也許現在將看着很容易了。有幾種思路可以用來解決這個問題:
-
先求出所有可能的出棧序列,然后再一一判斷。(當然了,這種方法僅僅是為了提高自己的能力)
-
模擬入棧和出棧的過程,查看是否一致。詳細思路如下:
舉例:入棧序列為1,2,3,4,5 某一序列為 2,3,1,4,5 。首先選擇1 入棧,然后查看序列2 是否相同,不同說明沒有出棧,繼續入棧2,繼續查看 相同,說明2出棧,然后繼續查看是否相同1和3不同,繼續入棧3,查看和序列2中的頭元素3一致,出棧,繼續查看序列1中的1,和序列2中的1 一致,然后出棧。。。直到最終序列2為空;如果最后發現序列1為空的時候序列2中仍然有元素,則說明不是合法出棧序列;
-
這個比較簡單,假如在入棧序列中p<q<r 且p,q,r也是入棧的順序,那么在出棧序列中在r后面的比它小的元素按照降序排列。(這個可以在做題的時候快速判斷)
下面是我的代碼,采用的思路2
#include <iostream> #include <stack> using namespace std; int main(){ int arr[] = {1,2,3,4,5}; int arr2[] = {2,1,5,3,4}; stack<int> stk; int j = 0; for(int i = 0;i < 5;i++){ stk.push(arr[i]); if(stk.top()!=arr2[j])continue; while(stk.size()>0){ if(stk.top() == arr2[j]){ j++; stk.pop(); }else break; } } if(stk.size()!=0){ cout << "no" << endl; } else cout << " yes " << endl; return 0; }
對於卡塔蘭數的應用還有很多,發現它真的是一個很偉大的發現,有很多的問題都可以用它來解決,如果認真的研究它解決的所有這些問題,其實都和棧的序列都有一定的關系。