判斷是否是合法的出棧序列(詳細

在技術筆試面試上,我們常常會遇到這樣一類題型,如給你一個入棧序列,然後再讓你判斷幾個序列是否有可能爲它的出棧序列,如:

入棧序列爲 1 2 3 4 5,則 1 2 3 4 5可能爲它的出棧序列,而 5 4 1 2 3不可能爲它的出棧序列

對於n比較小的情況,我們往往可以通過手動模擬的方式來判斷,對於n比較大的時候,這種方法就顯得效率不佳了。

下面介紹一種通用的方法判定合法出棧序列,時間複雜度爲O(n)。爲了敘述方便,我們不妨設入棧序列爲 1 2 3.......n,並且每個元素各不相等。

事實上,一個出棧序列固定的話,那麼沒個數的出棧順序和時間都是固定的,則我們可以模擬棧的入棧出棧過程,來判斷是否一個合法的出棧序列。

我們首先設po爲目前爲止入棧的元素中最大的數,初始化爲0,若下一個出棧元素要大於po的話(設爲x),說明我必須將[po+1,x]中的所有書都入棧,再將x彈出即可(這時還應把po賦值爲x)。否則說明下一個出棧的元素已經在棧中,並且肯定是棧頂元素,若棧頂元素與下一個出棧元素不相等的話,我們可以判斷這不是一個合法出棧序列,否則,若所有的出棧元素都不引起衝突,則說明這是一個合法序列。這裏再說一下時間複雜度,因爲我們只有在下一個出棧元素大於po時,纔將元素壓入棧中,並且我們每一次判斷一個出棧元素是否發生衝突時,都會將棧頂元素彈出,所以每一個元素都入棧一次,出棧一次,所以時間複雜度爲O(n)。

算法的具體實現請看代碼。


  1. #include <stdio.h>
  2. #define maxn 1005
  3. int stack[maxn],top;
  4. int out[maxn];
  5. int check(int n)
  6. {
  7. int po=0;
  8. for(int i=1;i<=n;i++)
  9. {
  10. for(int j=po+1;j<=out[i];j++)
  11. {
  12. po=j;
  13. stack[top++]=j;
  14. }
  15. if(stack[--top]!=out[i])
  16. return 0;
  17. }
  18. return 1;
  19. }
  20. int main()
  21. {
  22. int n;
  23. scanf("%d",&n);//假設入棧序列爲1 2。。。。n
  24. for(int i=1;i<=n;i++)
  25. {
  26. scanf("%d",&out[i]);
  27. }
  28. if(check(n))
  29. printf("Yes\n");
  30. else
  31. printf("No\n");
  32. return 0;
  33. }



判斷是否是合法的出棧序列

棧,這個“後進先出(Last In First Out)” 數據結構應該都不陌生。如果 a、b、c 依次入棧,然後出棧,那麼出棧順序是 c、b、a;如果 a 入棧然後出棧、b 入棧出棧、c 入棧出棧,那麼出棧順序是 a、b、c。如果只是強調 a、b、c 的入棧順序,而不強調具體的出棧順序,那麼 cba 和 abc 都可以是出棧順序,acb、bac 和 bca 也都可以,而 cab 是不可以的:因爲 c 首先出棧說明 a b 在棧中,c 出棧後其他出棧順序只能是 ba 而不可能是 ab。

現在給 n 個數或是字母,假定就是 1、2、3、...、n ,已知它們是按照順序入棧的,有幾個問題:

  1. 給定一個序列,判斷是否可能是一個出棧序列?比如 1 2 3 ... n 肯定可以,n (n-1) ... 3 2 1 也可以,但是 1 4 2 ... 就不可以;
  2. 合法的出棧序列有多少種 ?

模擬入棧出棧

me 們可以模擬一下入棧出棧操作,如果可以就是 yes,如果不可以就是 no ! 但如何模擬呢 ? 舉個 1 2 3 4 5 的出棧例子 4 5 3 2 1 。me 們建一個輔助棧(最初是空的)再加一個帶入棧元素 in (最初是 1 ),然後看判斷序列 4 5 3 2 1。

  1. 待入棧元素 in = 1,當前判斷序列元素是 4,1 ≠ 4 那麼, 1 入棧,然後 in = 2;2 ≠ 4 然後 2 入棧,然後 3 入棧;然後 in = 4;
  2. in = 4,那麼應該是 4 入棧然後出棧,這裏直接可以將當前判斷元素換成下一個也就是 5 ;
  3. 4 出棧以後, me 們發現棧頂 top 是 3,不匹配 5,這個時候沒法繼續出棧;那麼執行和第一步類似的操作,使用 in 去判斷;
  4. in = 5 匹配第二個判斷元素,那麼 5 入棧出棧(直接看下一個判斷元素),這個時候棧頂 top = 3,3 可以出棧,然後棧頂是 2 可以出棧,然後是 1 可以出棧;最後判斷序列元素全部判斷完了,那麼說明序列 4 5 3 2 1 是一個合法的出棧序列;

模擬過程基本如上:最初棧爲空,in = 1;然後依次掃描判斷序列元素 e,如果和 in 不同則需要不斷將 in 入棧(因爲當前棧中元素並不匹配 e);如果 in 和 e 相同則直接判斷下一個元素(可以認爲是 e 先入棧然後出棧),這個時候考慮待判定元素 e 是否可以通過出棧匹配,如果可以則出棧,而且是儘可能多的出棧,如果不可以則有通過繼續將 in 元素壓入棧中尋求匹配。如果判定序列的元素都判定過了,那就是 yes;如果麼法出棧,而 in 又麼法繼續入棧(比如 in 已經超過 n 了),那就是 no !

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. bool test_ok(int n, vector<int>& olist);
  5. int main(int argc, char *argv[])
  6. {
  7. vector<int> olist;
  8. int n, x, count=0;
  9. bool ok;
  10. while(1){
  11. olist.clear();
  12. cin >> n;
  13. if(!cin)
  14. break;
  15. for(int i=0; i<n; ++i){
  16. cin >> x;
  17. olist.push_back(x);
  18. }
  19. ok = test_ok(n, olist);
  20. if(ok)
  21. ++count;
  22. cout << ok << '\n';
  23. }
  24. cout << "count : " << count << endl;
  25. return 0;
  26. }
  27. bool test_ok(int n, vector<int>& olist)
  28. {
  29. vector<int> istack;
  30. int in = 1, top, oindex = 0;
  31. while(1){
  32. if(oindex >= n) // ok, olist has no element left !
  33. return true;
  34. if(in > n)
  35. return false;
  36. if(in != olist[oindex]){ // push into stack
  37. istack.push_back(in);
  38. ++in;
  39. continue;
  40. }
  41. ++in;
  42. ++oindex;
  43. while(!istack.empty() && istack.back() == olist[oindex]){ // pop from stack
  44. istack.pop_back();
  45. ++oindex;
  46. }
  47. }
  48. }

全排列

上面提的第二個問題還沒有回答,不過如果 me 們已經可以判斷序列了,那麼將所有的序列都判斷一遍然後數數有多少個合法的,不就可以了 ? 那麼 1、2、3、...、n 的所有序列有多少種呢 ? 好吧,這就是一個全排列丫,有木有 !

1、2、3、...、n 的全排列有 n! 種,這個大家都知道的結論就不多說了。問題是,如何生成 n 個數的全排列,這是這裏關係的重點。其實 me 這裏並不關心如何實現,只是想寫個程序生成 n 個數的全排列而已,不錯的是,c++ 標準庫已經提供了類似的函數,very good !

生成 1-n 的全排列

程序掃描一個數字 n,然後生成其所有的全排列,實際就是 n! 個序列,而每個序列以 n 打頭,這樣的好處就是,程序的結果可以直接傳遞給上面的程序使用 !

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. using namespace std;
  5. int main(int argc, char *argv[])
  6. {
  7. vector<int> vints;
  8. int n;
  9. cin >> n;
  10. for(int i=0; i<n; ++i)
  11. vints.push_back(i+1);
  12. do {
  13. cout << n;
  14. for(int i=0; i<n; ++i)
  15. cout << ' ' << vints[i];
  16. cout << '\n';
  17. } while(next_permutation(vints.begin(), vints.end()));
  18. cout << endl;
  19. return 0;
  20. }



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章