java-二叉树遍历(前中后层次)

package com.areio.offer;

public class TreeNode {
    //每一个结点要包含左指针,右指针,当前结点的值
    int val;
    TreeNode left = null;
    TreeNode right = null;
    //把左右节点设为null,构造新结点时不用自己设置
     public TreeNode(int val){
         this.val=val;
     }
}
package com.areio.offer;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

/*
* 前序遍历:根,左,右
* 中序遍历:左,根,右
* 后序遍历:左,右,根
* 层次遍历:第一层访问完再访问下一层
* */
public class _7A_TreeVisit {
    /*创建一个完全二叉树
    * 9  6  6`  5  16 2
    *
    *       9
    *   6       6`
    * 5  16   2
    *
    * 对非叶子结点建立其左右孩子,最后一个非叶子结点可能没右孩子,单独处理
    * 完全二叉树有n/2个非叶子结点
    *
    * 前序:9 6 5 16 6` 2
    * 中序:5 6 16 9 2 6`
    * 后序:5 16 6 2 6` 9
    * 层次:9 6 6` 5 16 2
    * */

    private static List<TreeNode> nodeList = null;
    public static void createTree(int[] nums){
        //将数组的元素全部转为结点
        nodeList=new LinkedList();
        for (int i=0;i<nums.length;i++){
            nodeList.add(new TreeNode(nums[i]));
        }
        //建立前n/2-1个非叶子结点,最后一个单独处理
        for (int i=0;i<nums.length/2-1;i++){
            //i都是可以作为某子树根节点的元素
            //构建左孩子
            nodeList.get(i).left=nodeList.get(2*i+1);
            //构建右孩子
            nodeList.get(i).right=nodeList.get(2*i+2);
        }
        //单独处理最后一个非叶子结点
        //处理左孩子(一定有)
        int lastParentIndex=nums.length/2-1;
        nodeList.get(lastParentIndex).left=nodeList.get(lastParentIndex*2+1);
        //是否有右孩子?
        if (nums.length%2==1){
            nodeList.get(lastParentIndex).right=nodeList.get(lastParentIndex*2+2);
        }

    }
    /*
    * 前序递归版本
    *
    * 这是一个前序遍历的方法:先访问根,再访问完根的整颗左子树,最后访问根的整颗右子树;
    * 每颗子树也需要按照前序遍历的方法——复用同个方法,只是子树的根换了而已
    * */
    public static void preOrder1(TreeNode root){
        //递归结束条件
        if (root==null){
            return;
        }
        //1、访问根
        System.out.print(root.val+" ");
        //2、访问根的整颗左子树,左子树也按前序遍历方法来遍历
        preOrder1(root.left);
        //3、访问根的整颗右子树,右子树也按前序遍历方法来遍历
        preOrder1(root.right);

    }
    /*
     * 前序非递归版本
     * 图解查看:https://blog.csdn.net/u012535132/article/details/84309406
     * 将当前结点入栈,弹出结点并访问,再将弹出结点的右孩子,左孩子分别入栈;这样先出来的是左孩子
     * 假设输入 9 6 6` 5 16 2,其先序遍历应该是9 6 5 16 6` 2
     * */
    public static void preOrder2(TreeNode root){
        if (root==null) return;
        Deque<TreeNode> stack=new ArrayDeque<>();

        stack.push(root);
        while (!stack.isEmpty()){
            //出站并访问,将其右左结点入栈
            TreeNode ele = stack.pop();
            System.out.print(ele.val+" ");
            if (ele.right!=null){
            stack.push(ele.right);
            }
            if (ele.left!=null) {
                stack.push(ele.left);
            }
        }

    }
    /*
     * 中序递归版本
     *
     * 这是一个中序遍历的方法:先访问根的整颗左子树,再访问根,最后访问根的整颗右子树;
     * 每颗子树也需要按照中序遍历的方法——复用同个方法,只是子树的根换了而已
     * */
    public static void inOrder1(TreeNode root){
        //递归结束条件
        if (root==null) return;
        //1、先访问根的整颗左子树
        inOrder1(root.left);
        //2、访问根
        System.out.print(root.val+" ");
        //3、访问根的整颗右子树
        inOrder1(root.right);
    }
    /*
     * 中序非递归版本
     * 图解查看:https://blog.csdn.net/u012535132/article/details/84329729
     * 1、将某树/子树的root入栈,只要有左孩子就一直压入栈;
     * 2、开始出栈并访问,判断其是否有右孩子,有则将这一个右孩子入栈(一旦一个元素加入栈,就需要先将其所有左节点入栈)
     * 3、下一次出栈就是这个右孩子(作为根的身份),会去重复1、2、3
     * 假设输入 9 6 6` 5 16 2,其中序遍历应该是5 6 16 9 2 6`
     * */
    public static void inOrder2(TreeNode root){
        if (root==null) return;
        Deque<TreeNode> stack=new ArrayDeque<>();

        //p是当前结点
        TreeNode p=root;
        stack.push(root);
        while (!stack.isEmpty()){
            //只要有一个新元素入栈,就将其所有左孩子就全部入栈(左孩子全部遍历完才到它自己)
            //1、左节点入栈
            while (p!=null&&p.left!=null){
                stack.push(p.left);
                p=p.left;
            }
            //由于左孩子先访问,可以直接出栈并访问
            //2、根(无论左右结点最终都会变成根)
            p = stack.pop();
            System.out.print(p.val+" ");
            //3、右结点入栈
            //判断当前出栈元素有没有右节点,只加入一个,将其所有的左孩子遍历完再到自己
            if (p.right!=null){
                stack.push(p.right);
                p=p.right;
            }else {
                p=null;//标志没有新结点入栈,有的话下一次循环需要将其左节点全部入栈
            }

        }

    }
    /*
     * 后序递归版本
     *
     * 这是一个后序遍历的方法:先访问根的整颗左子树,访问根的整颗右子树,最后再访问根;
     * 每颗子树也需要按照后序遍历的方法——复用同个方法,只是子树的根换了而已
     * */
    public static void postOrder(TreeNode root){
        //递归结束条件
        if (root==null){
            return;
        }
        //1、先访问根的整颗左子树
        postOrder(root.left);
        //2、访问根的整颗右子树
        postOrder(root.right);
        //3、访问根
        System.out.print(root.val+" ");

    }
    /*
    * 后序非递归:把所有左节点入栈,一个个出栈,只要其右节点没访问,当前结点就不可访问
    * 1、把当前根的所有子节点入栈,
    * 2、然后开始peek,判断peek的这个元素是否没有右子树或者右子树已被访问,
    * 3、如果都是,那么可以出栈访问,
    * 4、如果有右子树且右子树未访问,则把该右子树的根节点入栈,
    * 5、重复1、2、3、4
    *
    *
    * */
    public static void postOrder2(TreeNode root){
        if (root==null) return;
        Deque<TreeNode> stack=new ArrayDeque<>();

        //p是当前结点,pre是上次访问的结点
        TreeNode p=root;
        TreeNode pre=null;
        //根先入栈
        stack.push(p);
        while (!stack.isEmpty()){
            //只要有一个新元素入栈(没有新元素则后面会设置p=null),有左孩子,就把左孩子入栈
            //1、左节点全部入栈
            while (p!=null&&p.left!=null){
                stack.push(p.left);
                p=p.left;
            }
            //先读取栈顶元素,把每个元素当做根,判断其右孩子是否已访问,访问了才到自己
            p=stack.peek();//①与中序的区别:先试探访问其右孩子是否已访问,后面才能出栈
            //无右子树或右子树已被访问过,这个时候才可以出栈
            if (p.right==null||p.right==pre) {//②与中序遍历的区别:右子树访问完才能到根
                System.out.print(p.val+" ");
                //3、访问根(无论左右结点最终都会变成根)
                 stack.pop();
                pre=p;
                p=null;//p=null标记没有新结点加入,则不需要加入新节点的左孩子,继续出栈,

            }else {//有右节点且右节点还未被访问,则右孩子入栈
                //2、右节点入栈
                stack.push(p.right);
                p=p.right;

            }
        }


    }

    /*
    * 层次遍历:一个元素一出队列,就将其左右孩子入队
    *
    * */
    public static void levelOrder(TreeNode root){
        if (root==null) return;

        Deque<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);
        //当前结点
        TreeNode p=root;

        while (!queue.isEmpty()){
            //出队,与下面语句不可换,否则p会读到上次元素
            p=queue.poll();
            System.out.print(p.val+" ");

            //把出队元素的左右孩子入队
            if (p.left!=null)
                queue.offer(p.left);
            if (p.right!=null)
                queue.offer(p.right);
        }
    }


    public static void main(String[] args) {
        //创建一个完全二叉树
        createTree(new int[]{9,6,6,5,16,2});
        System.out.println("前序递归:");
        preOrder1(nodeList.get(0));
        System.out.println("\n前序非递归:");
        preOrder2(nodeList.get(0));
        System.out.println("\n中序递归:");
        inOrder1(nodeList.get(0));
        System.out.println("\n中序非递归:");
        inOrder2(nodeList.get(0));
        System.out.println("\n后序递归:");
        postOrder(nodeList.get(0));
        System.out.println("\n后序非递归:");
        postOrder2(nodeList.get(0));
        System.out.println("\n层次非递归:");
        levelOrder(nodeList.get(0));


//        Deque<TreeNode> stack=new ArrayDeque<>();
//        stack.push(new TreeNode(9));
//        stack.push(new TreeNode(6));
//        stack.push(new TreeNode(6));
//        stack.push(new TreeNode(5));
//        stack.push(new TreeNode(16));
//        stack.push(new TreeNode(2));
//
//        System.out.println(stack.peek().val);//2  读取最后进去的
//        System.out.println(stack.peekLast().val);//9  读取最先进去的
//        System.out.println(stack.pop().val);//2读取并删除最后进去的
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值