二叉树的遍历(前序、中序、后序、层序遍历)

二叉树遍历(前序,中序,后序,层序)

本文主要讲前中后序的递归和非递归方法以及层序遍历方法。
所有代码使用Java编写
首先给出二叉树节点类

    static class ListNode {
        int val;
        ListNode left;
        ListNode right;

        ListNode() {
        }

        ListNode(int val) {
            this.val = val;
        }

    }

其中测试的二叉树的结构和应该得到的结果如下

        //                1
        //              /  \
        //             2    3
        //           /  \    \
        //          4    5    6
        //              / \
        //            7    8
// preOrderTraverse 前序 1 2 4 5 7 8 3 6
// medOrderTraverse 中序 4 2 7 5 8 1 3 6
// postOrderTraverse 后序 4 7 8 5 2 6 3 1

三种顺序的遍历方法

先序:考察到一个节点后,即刻输出该节点的值,并继续遍历其左右子树。(根左右)

中序:考察到一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。(左根右)

后序:考察到一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。(左右根)

先序遍历

递归方法
递归的方法比较好理解,先打印当前节点的值,然后再遍历左右子树,中序和后序的递归方法同以下方法是类似的。

// preOrderTraverse 前序 1 2 4 5 7 8 3 6
    //前序遍历
    //递归
    public static void preOrderTraverse1(ListNode root) {
        if (root != null) {
            System.out.print(root.val + " ");
            preOrderTraverse1(root.left);
            preOrderTraverse1(root.right);
        }
    }

非递归方法
遍历一个树需要使用栈来存储节点,这里使用LinkedList来作为栈,其中一些方法需要仔细理解

  1. push()方法:将节点压入栈顶,等同于addFisrt()方法,仔细看代码的话会发现push()方法调用的就是addFirst()方法。
  2. addLast()方法:将节点压入栈尾。
  3. pop()方法:将节点从栈顶中取出(意思是将栈顶元素取出,并删除栈顶中相应的节点)。
  4. peek()方法:将节点从栈顶借出(意思是复制一个元素,但是不删除相应的栈顶元素)。

这里的思路是这样的,使用栈来存储每次需要入栈的根节点(左子树、右子树的根节点)。因为先序遍历是先打印当前节点再访问左右子树,故可以使用动态节点来判断当前节点的左子树情况,即当前节点左子树是否存在?直至不存在再去判断右子树。
这里有一个重要的地方是:当左子树的根节点是null时,需要继续访问右子树,这里利用栈 pop出栈顶节点,因为这个栈顶节点已经访问过并且打印出来了。

    //前序遍历
    //非递归
    public static void preOrderTraverse2(ListNode root) {
        LinkedList<ListNode> stack = new LinkedList<>();
        //动态指针 用于 锁定当前节点
        ListNode pNode = root;
        while (pNode != null || !stack.isEmpty()) {
            //分开两段
            if (pNode != null) {
                //打印当前节点
                System.out.print(pNode.val + " ");
                //当前节点入栈
                stack.push(pNode);
                //更新指针节点
                pNode = pNode.left;
            }
            //如果当前指针节点为空,从 栈中 出一个节点 继续作为指针节点
            else {
                ListNode node = stack.pop();
                pNode = node.right;
            }
        }
    }

中序遍历

// medOrderTraverse 中序 4 2 7 5 8 1 3 6

递归方法

    //中序遍历
    //递归
    public static void medOrderTransverse1(ListNode root) {
        if (root != null) {
            medOrderTransverse1(root.left);
            System.out.print(root.val + " ");
            medOrderTransverse1(root.right);
        }
    }

非递归遍历
这里的思路参考前序遍历,这里转换为左中右,故当左子树为null的时候,我们利用pop方法从栈中提取栈顶节点(因为左子树为空,故栈顶节点为左中右的中且存在,故可以打印,在继续判断栈顶节点的右子树是否存在即可)

    //中序遍历
    //非递归
    public static void medOrderTransverse2(ListNode root) {
        LinkedList<ListNode> stack = new LinkedList<>();
        ListNode pNode = root;
        while (pNode != null || !stack.isEmpty()) {
            if (pNode != null) {
                //将当前节点加入 栈中
                stack.push(pNode);
                //将动态指针重新指向当前节点的 左节点
                pNode = pNode.left;
            } else {
                ListNode node = stack.pop();
                //打印当前节点
                System.out.print(node.val + " ");
                pNode = node.right;
            }

        }
    }

后序遍历

  1. 后序遍历不同于上述两种方法,后序遍历需要访问完左右子树后才可以访问当前节点,那么如何确定左右子树都访问完了呢?需要设置一个节点来确定,这里设置lastVisit。
  2. 判断方法为,若lastVisit等于当前考察节点的右子树,就表明当前节点的左右子树已经遍历完成,那么就可以输出当前节点。
  3. 同时将lastVisit节点设置为当前节点,将动态指针节点设置为null,下一轮继续访问栈顶元素即可
  4. 如果lastVisit节点不等于当前节点的右子树,那么据需要继续判断右子树,pNode = pNode.right
  5. 这里使用peek()方法查看栈顶元素,而不是取出栈顶元素,方便确定栈顶元素的右子树是否为null,不为null则栈顶元素不需要取出,为null则取出
// postOrderTraverse 后序 4 7 8 5 2 6 3 1

递归方法

    //后序遍历
    //递归
    public static void postOrderTraverse1(ListNode root) {
        if (root != null) {
            postOrderTraverse1(root.left);
            postOrderTraverse1(root.right);
            System.out.print(root.val + " ");
        }
    }

非递归方法

    public static void post(ListNode root) {
        LinkedList<ListNode> stack = new LinkedList<>();
        ListNode pNode = root;
        ListNode lastVisit = root;
        while (pNode != null || !stack.isEmpty()) {
            if (pNode != null) {
                stack.push(pNode);
                pNode = pNode.left;
            } else {
                ListNode node = stack.peek();
                ListNode right = node.right;
                if (right == null || right == lastVisit) {
                    System.out.print(node.val + " ");
                    stack.pop();
                    lastVisit =node;
                    pNode = null;
                }
                else {
                    pNode = node.right;
                }
            }
        }
    }

层序遍历

这里使用的是addLast()方法,方便加入到栈尾,这样每次出栈的点都是从上层开始的

    //层序遍历
    public static void levelTraverse(ListNode root) {
        if (root == null)
            return;
        //用于存储节点
        LinkedList<ListNode> stack = new LinkedList<>();
        //存储根节点
        stack.push(root);
        while (!stack.isEmpty()) {
            ListNode node = stack.pop();
            System.out.print(node.val + " ");
            if (node.left != null)
                stack.addLast(node.left);
            if (node.right != null)
                stack.addLast(node.right);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值