二叉树的前序、中序、后序三种遍历方式(Java实现)

定义

首先我们要知道前序遍历、中序遍历、后序遍历三种遍历方式的定义

  • 前序遍历是根->左->右
  • 中序遍历是左->根->右
  • 后序遍历是左->右->根

根据定义,我们很容易发现规律,前中后分别指的是根的访问顺序,比如前序,就是根在最前面,最先访问根节点再左然后再右,中序就是根节点在中间访问,后序就是根节点最后访问,但是无论哪种遍历方式,左子节点永远比右子节点先访问

假设有下面一颗二叉树
在这里插入图片描述
那么:
前序遍历结果为:8,6,4,3,5,7,10,9,11
中序遍历结果为:3,4,5,6,7,8,9,10,11
后序遍历结果为:3,5,4,7,6,9,11,10,8

可以发现在中序遍历搜索二叉树的时候,结果是有序的,所以一般输出搜索二叉树的所有节点的时候会采用中序遍历的方式

前序遍历描述:
对照着上面的图来讲解
从根节点8出发,访问左子节点6,然后左子节点6的左子节点4,将路过的节点依次输出,直到到达叶子节点3,然后再访问叶子节点的父节点4的右子节点5,如果5有左子节点那么继续访问5的左子节点,当4的右子节点遍历完成了之后,在向上找4的父节点6的右子节点
总结:有左就先左,直到最底层,左子树全部遍历完成了,再找父节点的右子树
画个图好理解一点,箭头就是前序遍历的访问顺序
在这里插入图片描述

中序遍历描述:
先遍历左子树,然后遍历自己,最后遍历右子树,左子树没遍历完绝对不遍历自己,自己没遍历过的话,绝对不遍历右子树
子树的遍历也满足前面的逻辑
按照这个逻辑一定会先递归到最左边的叶子节点,然后最左叶子节点遍历完成,然后遍历父节点,父节点遍历完了,在遍历右边的
总结:有左就左,然后自己,然后右边

后序遍历描述:
先遍历左子树,然后再遍历右子树,就是如果有子节点没遍历过或者子树没有遍历完,那么绝对不遍历自己,遍历子树的时候也满足上面的规则
根据这个规则,那么一定是先怼到最左边的节点,可以肯定的是最左边的节点肯定是没有左子节点了,那么有下面两种情况
假设1:最左边的节点有右子节点,根据子节点优先,有一个子节点的先输出子节点
假设2:最左边的节点没有任何子节点,输出自己
两个假设的情况整完了,开始找父节点的右子树,如果父节点有右子树,那么将右子树也按照左右中的这个逻辑来遍历

定义Node节点和树

就简单的定义一下

public class Tree{
	static class Node<T>{
        private Node left;
        private Node right;
        private T data;
        public Node(T t){
            this.data = t;
        }
    }
    private static Node head;
}

前序遍历代码实现

递归

    private static List<Object> preOrder(Node node){//先序递归遍历
        List<Object> list = new LinkedList<>();
        if(node!=null){
            list.add(node.data);//先自己
            list.addAll(preOrder(node.left));//再左边
            list.addAll(preOrder(node.right));//再右边
        }
        return list;
    }

    public static List<Object> preOrder(){//调用先序递归遍历
        return preOrder(head);
    }

非递归,使用栈

其实递归的原理就是栈,先进先出,所以用栈也可以实现非递归的方式遍历

	private static List<Object> sqlPreOrder(){//先序非递归
        List<Object> list = new LinkedList<>();
        Stack<Node> stack = new Stack<>();
        Node p = head;
        while (p != null || !stack.isEmpty()){
            if(p != null){//如果当前节点不为空
                list.add(p.data);//先输出自己
                stack.push(p);//将自己压入栈
                p = p.left;//访问左节点
            }else {//如果当前节点为空
                p = stack.pop();//那么弹出栈顶元素
                p = p.right;//访问栈顶元素的右节点
            }
        }
        return list;
    }
    public static List<Object> sqlPreOrder(){//调用先序非递归遍历
        return sqlPreOrder(head);
    }

中序遍历代码实现

递归

    private static List<Object> inOrder(Node node){//中序递归遍历
        List<Object> list = new LinkedList<>();
        if(node!=null){
            list.addAll(inOrder(node.left));
            list.add(node.data);
            list.addAll(inOrder(node.right));
        }
        return list;
    }

非递归,同样用栈

先序遍历的时候是先访问自己再入栈,那么中序就是先入栈,再访问左节点,再入栈,再访问左节点,直到没有左节点,那么弹出栈顶,其实此时的栈顶就是自己,遍历自己,然后遍历右节点,右节点重复操作,直到右节点为空,或者右节点都遍历过,那么再弹就是自己的父节点了

	public static List<Object> sqlInOrder(){//中序非递归
        List<Object> list = new LinkedList<>();
        Stack<Node> stack = new Stack<>();
        Node p = head;
        while (p != null || !stack.isEmpty()){
            if(p != null){
                stack.push(p);
                p = p.left;
            }else {
                p = stack.pop();
                list.add(p.data);
                p = p.right;
            }
        }
        return list;
    }

后序遍历代码实现

递归

	private static List<Object> postOrder(Node node){//后序递归遍历
        List<Object> list = new LinkedList<>();
        if(node!=null){
            list.addAll(postOrder(node.left));
            list.addAll(postOrder(node.right));
            list.add(node.data);
        }
        return list;
    }
    public static List<Object> postOrder(){//调用后序递归遍历
        return postOrder(head);
    }

非递归

后序的非递归实现有点复杂,下次再写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值