栈的压入、弹出序列

栈的压入、弹出序列

最近在刷力扣时看到了这个题的一个有意思的解法,在这里记录一下。

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

解法一:

很轻易就能想到,我们可以用一个栈来辅助我们进行操作,我们通过依次比较栈的弹出节点和popped数组的顺序从而进行判断,这里便不再多说。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        LinkedList<Integer> stack = new LinkedList<>();
        int i = 0;
        for (int num : pushed) {
            stack.push(num);
            while (!stack.isEmpty() && stack.peek() == popped[i]) {
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

解法二:

第二个解法是看到了讨论中的一位大神说的,我觉得这个解法挺有意思,便记录下来。

首先我们要知道的便是:一组合法的入栈序列和出栈序列对应着一个二叉树的先序和中序,我们可以通过二叉树的迭代方法来思考,在用迭代法进行二叉树的遍历时,在先序遍历中,我们通常是先访问节点,然后再将节点压入栈中,而中序遍历时则是先将节点出栈,然后再访问节点,由此便是可以得知入栈对应先序,出栈对应中序。

这里很容易陷入一个误区,同一个入栈顺序可能对应卡特兰数个出栈顺序,但是一棵树只能对应一个中序序列,而且,如果有中途出栈再入栈的情况,这个出栈序列一定是中序嘛?要注意的是,我们要找的树是符合先序遍历的一棵树,那么我们便不能判断树的具体形状,一个树只有一个先序和中序序列,但是一个先序序列对应着不同的树,也就对应着不同的中序序列,也就是不同的出栈顺序。

再详细的说一下为什么按先序序列进栈,出栈顺序一定是中序序列。在我们进行遍历时,通常会使用的就是递归遍历,但递归本质上也是系统帮我们压栈,所以我们通过迭代自己压栈来详细的看一下。

一开始我们先访问根节点,然后对根节点操作完后要将根节点压栈,因为我们还需要根节点来对根节点的右子树进行处理,所以根节点要入栈等待,然后开始处理根节点的左子树,同样的操作,也是先处理左子树的根节点然后根节点入栈,一直到左子树为空时,我们先将栈中第一个元素弹出,然后对弹出的节点的右子树进行操作,同样的处理逻辑,根节点,左子树,右子树,处理完后处理栈中下一个弹出节点的右子树,一直到栈为空,那么整颗树就处理完了,这就是先序遍历。而中序遍历的时候我们是直接将节点入栈,因为中序遍历的处理顺序是左中右,所以我们要先处理左子树,而根节点则要先等待,一直入栈到左子树为空,然后处理该节点,之后栈中第一个元素不要弹出,要等到栈中第一个元素的右子树也按照相同处理方式处理完才能出栈,出栈后再对该节点进行操作,这就是中序遍历。

这里直接引用一位大神的总结:

  1. 对于一棵树,按先序遍历进栈,然后出栈得到的一定是中序序列
  2. 一棵树的中序序列可以由按先序序列进栈的情况下的一种出栈顺序得到

什么意思呢,第一点是我们有了一个确定的树形,比如说我已经给了你一颗明确的树,然后你按照先序遍历的顺序进栈,出栈的顺序一定就是中序遍历的顺序。第二点则是我们没有一颗明确给出的树,我们只是知道它的先序序列是什么,那么这颗树的中序遍历的顺序就是这个先序序列进栈的情况的一种出栈顺序,这两点一定要搞清楚。

class Solution {
	public boolean validateStackSequences(int[] pushed, int[] popped) {
        return isTree(pushed, popped, 0, pushed.length - 1, 0);
    }

    public boolean isTree(int[] pushed, int[] popped, int left, int right, int rootPos) {
        if (left > right) {
            return true;
        }
        int rootVal = pushed[rootPos];
        int pos = -1;
        for (int i = left; i <= right; i++) {
            if (popped[i] == rootVal) {
                pos = i;
                break;
            }
        }
        if (pos == -1) {
            return false;
        }
        return isTree(pushed, popped, left, pos - 1, rootPos + 1) &&
                isTree(pushed, popped, pos + 1, right, rootPos + pos - left + 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值