• 【学习笔记--数据结构】合法的出栈序列与栈混洗


    在大学学过数据结构课的人相信都对这样一个问题不陌生,描述大致如下:

      某个程序可以进行一系列入栈和出栈的混合操作。每次入栈操作将整数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. 实现:可以按照上面的代码实现。

  • 相关阅读:
    闭包
    保存数据
    Browers Object Model
    JavaScript中的null和undefined
    魔法查询函数
    《黑客与画家》 读书感想
    CakePHP查询数据
    HDFS写入和读取流程
    回调函数
    JAVA中的指针
  • 原文地址:https://www.cnblogs.com/helenawang/p/5512053.html
Copyright © 2020-2023  润新知