算法实验室-18-后序遍历的邪道与正道

三序遍历的遍历顺序

先序:根->左->右(非常简单,一个深度搜索搞定,逻辑清晰)

中序:左->根->右(算法比较复杂)

后序:左->右->根(邪道很简单,基于反向先序的逆序,借助双栈,正道借助单栈)

 

后序遍历的邪道算法

算法的基本灵感:

如图所示:

先序遍历是:ABDEGCFHI

后序遍历是:DGEBHIFCA

我们往后的实验场景通常会基于这个场景,所以我们一般需要先构造出这样一个树:

package BinaryTree;

public class BinaryTreeCase {



    public BinaryTreeNode tree_1(){
        BinaryTreeNode A = new BinaryTreeNode("A");
        BinaryTreeNode B = new BinaryTreeNode("B");
        BinaryTreeNode C = new BinaryTreeNode("C");
        BinaryTreeNode D = new BinaryTreeNode("D");
        BinaryTreeNode E = new BinaryTreeNode("E");
        BinaryTreeNode F = new BinaryTreeNode("F");
        BinaryTreeNode G = new BinaryTreeNode("G");
        BinaryTreeNode H = new BinaryTreeNode("H");
        BinaryTreeNode I = new BinaryTreeNode("I");
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        E.left = G;
        C.right = F;
        F.left = H;
        F.right = I;
        return  A;
    }


}

public class BinaryTreeNode {

    public BinaryTreeNode left;
    public BinaryTreeNode right;
    public int value;
    /**
     * 字符变量
     */
    public String sVal;
    public BinaryTreeNode(String vl){
        sVal = vl;
    }

}

先来做一个简单的先序遍历preOrder();

    public static List<String> preOrder(BinaryTreeNode head){

        List<String> list = new ArrayList<String>();

        Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();

        stack.push(head);
        while (!stack.isEmpty()){

            BinaryTreeNode cur = stack.pop();
            list.add(cur.sVal);
            //出栈顺序:左->右
            //入栈顺序:右->左
            if(cur.right!= null){
                stack.push(cur.right);
            }
            if(cur.left!= null){
                stack.push(cur.left);
            }
        }
        return list;
    }

结果是:

然后,我们将入栈顺序变成了左->又

public static List<String> evilWaysOrder(BinaryTreeNode head){

        List<String> list = new ArrayList<String>();

        Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();

        stack.push(head);
        while (!stack.isEmpty()){

            BinaryTreeNode cur = stack.pop();
            list.add(cur.sVal);
            //出栈顺序:左->右
            //入栈顺序:右->左
            //邪道入栈:左->右
            if(cur.left!= null){
                stack.push(cur.left);
            }
            if(cur.right!= null){
                stack.push(cur.right);
            }

        }
        return list;
    }

结果变成:

然后和正确的后序遍历结果

你会发现,只需要进行简单的翻转就成

...所以这种方法非常邪道...

最终代码:

    public static List<String> evilWaysOrder(BinaryTreeNode head){

        List<String> list = new ArrayList<String>();

        Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
        Stack<String> reverseStack = new Stack<String>();
        stack.push(head);
        while (!stack.isEmpty()){

            BinaryTreeNode cur = stack.pop();

            reverseStack.push(cur.sVal);
            //出栈顺序:左->右
            //入栈顺序:右->左
            //邪道入栈:左->右
            if(cur.left!= null){
                stack.push(cur.left);
            }
            if(cur.right!= null){
                stack.push(cur.right);
            }

        }
        while (!reverseStack.isEmpty()){
            list.add(reverseStack.pop());
        }
        return list;
    }

 

这种方法,只能在面试的时候,突然忘了正道的后序遍历方法时,用的,从学习算法的角度来讲,不是非常推荐使用这种方法来进行操作

正道后序遍历

所谓道高一尺魔高一仗,正道的修行往往会比魔道更加复杂,哎

看着解法就很恶心,但是,就算你用一种偏门方法解决了这种“特情”,那么遇到更多的情况,你又凭借什么办法去适应呢?所以

正道功法虽然修行缓慢,但是却中正平和。

 

回顾后序遍历的顺序,左->右->根

DGEBHIFCA

不管是先序,还是后序,使用栈是最基本的,现在主要讨论的是入栈的顺序

这个时候,还是希望直觉能够起到一定作用,毕竟算法题除非灵光一闪,你突然悟了,你就真的懂了,不然,这个过程只能靠直觉去寻找防线

首先第一点,仅从答案DGEBHIFCA,我们是无法强行逆推入栈顺序的因为可能D如D出,E,G入,G,E出...不能直接想象成ACFIH...的入栈顺序去强行怼上去

 

得从原理来:后序遍历的顺序是

左->右->根

所以如果理解不了,强背吧,我也理解不了

压完左边压右边

右边也是一棵树

所以还得压左边

没点压了能出栈

栈顶变则右树出

新轮回栈压左边

无穷无尽是bug

添加记号禁重压

无穷右树具显化

好诗,好诗~

    public static ArrayList<String> postOrder3(TreeNode root) {
        TreeNode pointer = root;
        TreeNode banSign = null;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        ArrayList<String> list = new ArrayList<String>();
        while (!stack.isEmpty()||pointer!= null){

            if(pointer!= null){
                //压完左边
                stack.push(pointer);
                pointer = pointer.left;

            }
            if(pointer == null){

                pointer = stack.peek().right;
                //禁重压
                if(pointer!= null&&pointer!= banSign){
                    //压右边
                    stack.push(pointer);
                    //右边也是一棵树
                    //所以还得压左边
                    pointer = pointer.left;
                }else {
                    //没点压了能出栈
                    TreeNode temp =  stack.pop();
                    list.add(temp.sVal);

                    //栈顶变则右树出
                    pointer = null;

                    //添加标记
                    banSign = temp;
                }
            }
        }
        return list;
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值