数据结构与算法--举例分析法- 栈的压入弹出序列

举例分析
  • 与上两篇问中画图方法一样,我们可以用举例模拟的方法思考分析复杂问题。当一眼不能看出问题的规律的时候,我们可以用几个具体的例子来模拟一下问题的过程。这样就和我们在程序出现问题时候的debug一样,走一下整个流程,可以直观的看到整个过程。
  • 具体的例子还能帮助我们确保代码的质量,在编码完成后,可以将例子当做测试用例来模拟运行,看每一步操作后的结果和我们预期的是不是一样的。
包含min方法的栈
  • 题目:定义栈的数据结构,请在改类型中实现一个能够得到栈的最小元素min函数。在改栈中,调用min,push,pop的时间复杂度O(1)
  • 此处与普通栈不同点在于需要知道每次栈变动时候最小值。并且难点在于O(1)的时间复杂度,我们第一反应是标记最小值,这样可以在O(1)时间得到最小元素。但是最小值出栈后,次小的值就变成最小值,此时是无法获取这个值
  • 另外一个思路,每次入栈对栈中元素进行排序,这样能拿到最小值在栈首,会在尾。但是这样就违背了栈的后进先出的原则就不是栈了。
  • 分析到此处发现一个栈A并不能解决问题,我们用一个辅助的栈空间B,每次添加一个元素到A时候,将添加元素与最小元素比较(临时变量保存最小元素),将最小元素添加到B,即使最小元素没有变化仍然重复添加到B占位用。我们用如下案例分析:
步骤操作数据栈A辅助栈B最小值
1压入3333
2压入63,63,33
3压入43,6,43,3,33
4压入453,6,4,453,3,3,33
5压入03,6,4,45,03,3,3,3,00
6压入23,6,4,45,0,23,3,3,3,0,00
  • 如上表格,我们将最小元素每次都添加到辅助栈B中,就能保证辅助栈的栈顶是最小元素。

  • 当最小元素从数据栈弹窗,我们同时操作辅助栈弹窗,这样保证辅助栈下一个值是最小值。

  • 每一步操作数据栈,辅助站都同步,可以保证辅助栈顶永远都是数据栈中所有数据的最小值

  • 实现方法是在之前文章数据结构与算法–简单栈实现及其应用基础上完成。实现如下:

/**
 * 包含min方法的栈实现
 * @author liaojiamin
 * @Date:Created in 16:02 2021/4/2
 */
public class MyStackWithMin {
    private static MyStack dataStack = new MyStack();
    private static MyStack minStack = new MyStack();

    public static void push(int num){
        if(dataStack.size() == 0 && minStack.size() == 0){
            dataStack.push(num);
            minStack.push(num);
        }else {
            dataStack.push(num);
            if((int)minStack.getTop() > num){
                minStack.push(num);
            }else {
                minStack.push(minStack.getTop());
            }
        }
    }

    public static int pop(){
        if(dataStack.size() == 0 || minStack.size() == 0){
            return Integer.MAX_VALUE;
        }
        minStack.pop();
        return (int)dataStack.pop();
    }

    public static int min(){
        if(minStack.size() == 0){
            return Integer.MAX_VALUE;
        }
        return (int)minStack.pop();
    }

    public static void main(String[] args) {
        Random random = new Random();
        for (int i = 0; i < 100; i++) {
            int temp = random.nextInt(100);
            System.out.println(temp);
            push(temp);
        }
        System.out.println(min());
    }
}
栈的压入,弹出序列
  • 题目:输入两个整数序列,第一个序列表示栈的压入顺序,判断第二个序列是否可能是栈的弹出顺序,假设入站的所有数字均不相等,例如:1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是可能的一个出栈,但是4,3,5,1,2就不可能是出栈的方式
  • 我们依然用案例的方法分析,如下表格分析题中两种情况。
步骤操作弹出数字
1压入11
2压入21,2
3压入31,2,3
4压入41,2,3,4
5弹出1,2,34
6压入51,2,3,5
7弹出1,2,35
8弹出1,23
9弹出12
10弹出1
  • 如上表格中每一个步骤操作,我们在第五步骤时候,弹出4,压入5,之后持续弹出,就能得到对应的弹出序列。
  • 我们用同样的方式来递推第二个序列
步骤操作弹出数字
1压入11
2压入21,2
3压入31,2,3
4压入41,2,3,4
5弹出1,2,34
6弹出1,23
7压入51,2,5
8弹出1,25
9下一个弹出的是1,但是1 不在栈顶,压栈序列已经都入栈,操作无法继续
  • 如上两表格分析,入栈,出栈过程,我们可以判断一个序列是不是栈的弹出序列有如下规律:
    • 如果下一个弹出的数字正好是栈顶数字,那么直接弹出
    • 如果下一个弹出的数字不在栈顶,我们吧压栈序列中还没有入栈的数字压入辅助栈
    • 持续压入直到下一个需要弹出的数字压入栈顶位置
    • 如果所有数字都压入了栈但是还没找到下一个弹出的数字,那么该序列不存在一个弹出序列。
    • 综上有如下实现:
/**
 * 存在栈A的入栈系列S,判断给出的序列B是否可能是A 的出序列,例如1,2,3,4,5 入栈,4,5,3,2,1 出栈
 * @author liaojiamin
 * @Date:Created in 16:54 2021/4/2
 */
public class ValidateIsPopOrder {

    public static boolean validateIsPopOrder(int[] orderPush, int[] orderPop){
        if(orderPush == null || orderPop == null){
            return false;
        }
        if(orderPush.length != orderPop.length){
            return false;
        }
        MyStack myStack = new MyStack();
        int length = orderPop.length;
        int pushPosition = 0;
        int popPosition = 0;
        while (pushPosition < length || popPosition < length){
            while (myStack.size() > 0 && (int)myStack.getTop() == orderPop[popPosition] && popPosition < length){
                myStack.pop();
                popPosition++;
            }
            if(pushPosition < length){
                myStack.push(orderPush[pushPosition]);
                pushPosition ++;
            }
            if(pushPosition == length && myStack != null && (int)myStack.getTop() != orderPop[popPosition]){
                return false;
            }
        }
        return !(myStack.size() != 0);
    }

    public static void main(String[] args) {
        int[] push = {1,2,3,4,5};
//        int[] pop = {4,5,3,2,1};
//        int[] pop = {3,2,1,5,4};
        int[] pop = {4,3,5,1,2};
        System.out.println(validateIsPopOrder(push, pop));

    }
}
  • 以上两个问题都是比较复杂的问题,并且需要多个步骤分析才能得出结果,初看时候很少有思路的,这个时候,我们通过举例分析,一步一步来看,当最后一步符合,或者卡在某一个步骤时候,我们此时往往能从当前的状态看出解题的思路。

上一篇:数据结构与算法–解决问题的方法-顺时针打印矩阵
下一篇:数据结构与算法–广度优先打印二叉树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值