二叉树的前中后序,层序遍历详解


前言

本文对二叉树的前中后序,层序遍历的思路进行了解析,并给出了编写代码的思路和逻辑。包括前中后序的递归和非递归方法,实际上都是对栈的使用,层序遍历则是对队列的理解。


一、前中后序遍历

1.递归版本

  1. 前序遍历: 跟结点 -> 左子树 -> 右子树
  2. 中序遍历: 左子树 -> 跟结点 -> 右子树
  3. 后序遍历: 左子树 -> 右子树 -> 跟结点
    在这里插入图片描述
    因为递归版本比较简单,这里以前序遍历为例,其他两个类似。
    以上图中前序遍历结果为 FCADBEHGM
    代码也比较清晰。
    /**
     * 递归版
     */
    public static void preOrder(Node head) {
        if (head == null) {
            return;
        }
        System.out.print(head.val);//打印当前结点
        preOrder(head.left);//递归打印左子树
        preOrder(head.right);//递归打印右子树
    }

中后序遍历类似,不再赘述。

2.非递归版本

我们知道递归实际上是间接利用了方法栈来达到目的。
我们自己实现非递归版本就是要用栈。

我们以前序为例,无论是前中后序哪一种,不同的只是打印的时机而已。

前序遍历代码如下:

    public static void preOrder1(Node head) {
        System.out.println("preOrder1:");
        if (head == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        while (head != null || !stack.isEmpty()) {
            //一边往左树遍历,一边全部加入stack
            while (head != null) {
            	//打印的时机就是遍历的这个顺序
                System.out.print(head.val);
                stack.push(head);
                head = head.left;
            }
            //左树走到尽头,又往右边走。同时将该元素出栈。
            if (!stack.isEmpty()) {
                head = stack.pop().right;
            }
        }
    }

该方法的遍历顺序如图正好是 左->根->右,所以打印时机也比较好找。
在这里插入图片描述
同样的方法来中序遍历的话,如下,遍历的时机则变成了把左树全加入栈中后,右树加入栈之前。

 public static void midOrder1(Node head) {
        System.out.println("midOrder1:");
        if (head == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        while (head != null || !stack.isEmpty()) {
            while (head != null) {
                stack.push(head);
                head = head.left;
            }
            if (!stack.isEmpty()) {
                head = stack.pop();
                System.out.print(head.val);
                head = head.right;
            }
        }
    }

同样的方法来后序遍历的话,还需要额外的一个变量来记录右子树

    public static void postOrder1(Node head) {
        System.out.println("postOrder1:");
        if (head == null) {
            return;
        }
        Node cur = head;
        Stack<Node> stack = new Stack<>();
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            if (!stack.isEmpty()) {
                cur = stack.pop();//遍历根节点之前
                Node r = cur.right;
                if (r != null) {
                //如果右树存在,先去遍历右树,遍历完将右树置空,
                    stack.push(cur);
                    cur.right = null;
                } else {//右树不存在或者右树打印过了,已经被置空
                    System.out.print(cur.val);
                }
                cur = r;
            }
        }
    }

可以看到,cur.right = null;这个代码是改变了二叉树的原结构,所以并不推荐这样做。下面介绍另一种解法。
这个解法是用head来记录了前置结点,可以来表示当前路线是从左树来的还是从右树来的,从而用不同的处理方法。

    public static void postOrder4(Node head) {
        System.out.println("postOrder4:");
        if (head == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        stack.push(head);
        Node cur;
        while (!stack.isEmpty()) {
            cur = stack.peek();//1.先拿出这个节点
            if (cur.left != null && cur.left != head && cur.right != null && cur.right != head) {
                //2.这个if表示后序遍历中左树还没走完的情况,就继续走左树
                //或者说 表示左边还有,且不是从左右子树返回来的   先把左边的全部加入栈
                stack.push(cur.left);
            } else if (cur.right != null && cur.right != head) {
                //3.这个if表示左树走完了的情况,现在去右树
                //或者说 表示是从左子树过来的,此时应该加入右子树
                stack.push(cur.right);
            } else {
                //4.这个if表示左右都走完了,现在将这个节点打印
                //或者说 表示要不是叶子结点要不就是右子树过来的,总之左右子树都遍历结束
                System.out.println(cur.val + " ");
                //5.把当前结点记录下来,表示下一个该遍历到的结点的前置结点
                head = cur;
            }
        }
    }

二、层序遍历

在这里插入图片描述
层序遍历的结果为 FCEADHGBM
方法很简单,用队列先进后出的特点,
过程如下

  1. 队列先放入F
  2. 在取出F之前,把C E放进队列,
  3. 取出C之前将A D放入队列。
  4. 依次进行下去就可以了。
    代码如下:
    /**
     * 层序遍历
     */
    public static void seqOrder(Node head) {
        if (head == null) return;
        System.out.println("seqOrder:");
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while (!queue.isEmpty()) {
            head = queue.poll();
            System.out.print(head.val);
            if (head.left != null)
                queue.add(head.left);
            if (head.right != null) {
                queue.add(head.right);
            }
        }
    }

总结

主要参考的是程序员代码面试指南这本书,做完题目后的一些思考和启发。加油吧!大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值