【學習筆記--數據結構】合法的出棧序列與棧混洗


在大學學過數據結構課的人相信都對這樣一個問題不陌生,描述大致如下:

  某個程序可以進行一系列入棧和出棧的混合操作。每次入棧操作將整數0到9中的一個元素按順序壓入棧,出棧操作打印彈出棧頂的整數。問給出的一個打印序列是否合法。

  這道題應該是數據結構考試的一道經典問題了。如果是在卷面上作答,我的做法是在紙上寫下所給的序列,同時畫一個空棧。然后將序列和棧頂元素“對拍”。如果無法從棧中彈出序列的當前元素,那么就是不合法的;對拍完成就是合法的。

  如果是編寫程序實現呢,其實完全去模擬手工實現的方法就可以。

  今天讀普利斯頓大學的那本橙色的《算法》書,在練習1.3.3中再次遇到了這道題,於是第一次編程實現了。代碼如下。

 1 package exercises;
 2 
 3 import edu.princeton.cs.algs4.Queue;
 4 import edu.princeton.cs.algs4.Stack;
 5 import edu.princeton.cs.algs4.StdOut;
 6 
 7 /**
 8  * @author Helena Wang
 9  * @version 0.0.1
10  * @function 判斷是否是合法的棧混洗序列
11  * @time 2018/7/18 16:50
12  */
13 public class StackOrderValidation {//TODO:write a blog
14     private Stack<Integer> stack;
15     private Queue<Integer> queue;
16     public StackOrderValidation() {
17 
18     }
19     public boolean validate(String string) {
20         stack = new Stack<>();
21         queue = new Queue<>();
22         String[] str = string.split(" ");
23         for (String s: str) {
24             queue.enqueue(Integer.parseInt(s));
25         }
26         int cur = -1; //當前入過棧的最大元素
27         while (!queue.isEmpty()) {
28 //            StdOut.println(queue);
29             while (cur < queue.peek()) {//嘗試讓棧頂和隊列頭的元素匹配
30                 cur++;
31                 stack.push(cur);
32             }
33 
34             if (queue.peek().equals(stack.peek())) { //匹配上,抵消掉
35                 queue.dequeue();
36                 stack.pop();
37             } else return false;//下一個元素不在棧頂,被壓着,序列不可能
38         }
39         return true;
40     }
41 
42     public static void main(String[] args) {
43         StackOrderValidation sov = new StackOrderValidation();
44         String[] strings = { //測試用例出自《算法4th》練習1.3.3
45                 "4 3 2 1 0 9 8 7 6 5",
46                 "4 6 8 7 5 3 2 9 0 1",
47                 "2 5 6 7 4 8 9 3 1 0",
48                 "4 3 2 1 0 5 6 7 8 9",
49                 "1 2 3 4 5 6 9 8 7 0",
50                 "0 4 6 5 3 8 1 7 2 9",
51                 "1 4 7 9 8 6 5 3 0 2",
52                 "2 1 4 3 6 5 8 7 9 0"
53         };
54         for (int i=0; i<strings.length; i++) {
55             StdOut.println(strings[i] + "====================");
56             StdOut.println(i + ": " + sov.validate(strings[i]));
57         }
58     }
59 }
View Code

  如代碼所示,利用一個棧和一個隊列。隊列用來存儲所給的待判斷合法性的序列,棧用來模擬題目中的棧。算法的主體在while循環中,當隊列中還有元素時,先試圖讓隊列和棧的位於頂部的元素能夠對上拍(當前已入棧過的最大元素不比隊列頭的元素小),然后嘗試消去隊列和棧的頂部元素;不能消去時則序列不合法。

  設n為元素個數,則算法的時間復雜度為O(n),空間復雜度為O(n)。

  

  看過幾本教材和習題集后,發現鄧老師的《數據結構》書中對這個問題的抽象更加體系化。我們把原始序列(0到9的整數)抽象為序列A,打印序列抽象為序列B,再加入一個刻畫入棧和出棧操作的中轉棧S,可以得到如下的棧混洗的概念。

 

一、 棧混洗的定義:

給定三個棧A, B, S。其中B, S初始為空,A含有n個元素,自頂向底構成序列:A=<a1, a2, ..., an]。

現只允許做以下兩種操作(保證pop時棧不空,即不發生下溢):

  1.S.push(A.pop())

  2.B.push(S.pop())

經過n次操作后,A, S均為空,A中元素轉入B中,此時B中的n個元素自底向頂構成的序列B=[ak1, ak2, ..., akn> 稱為原序列的一個棧混洗(stack permutation)。而每個棧混洗(符號序列)都對英語棧S的n次push和n次pop構成的操作序列。

 

二、對序列是否為合法的“棧混洗”序列的判定

1. 手工判定:設B為A={1, 2, ..., n}的任一排列,則有

  B是A的一個棧混洗    <=>    任意1<=i<j<k<=n,B中都不含以下模式:{..., k, ..., i, ..., j...}

2. 實現:可以按照上面的代碼實現。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM