二叉树的前中后序遍历及层次遍历

二叉树的前中后序遍历非递归实现及层次遍历队列实现 - 没有对象也没有new方法的大白 - CSDN博客

二叉树遍历(前序、中序、后序、层次、深度优先)递归和非递归 java实现 - 维 - CSDN博客

 

递归像剥洋葱,假设我们剥了一层皮后还要洗干净这层皮,这一步才算完成,那么我们在剥完一次皮后,本来按步骤要去洗的,但剥完发现还有一层皮要剥,于是我们就先不洗了,而是立马把新出现的皮给剥了,之后同样看到皮就一路剥下去,每层皮洗的动作都先延后,直到剥完最后一层皮,发现没皮可剥了,那我们就把最后一层皮拿去洗,这样我们对最后一层皮的处理就彻底完成了,完成后这层皮后出现再你手里的就是倒数第二层皮,然后你那它去洗,则第二层皮的处理就完成了,紧接着去洗倒数第三层…依次类推,直到洗好第一层皮那么整个动作就完成了.
前中后序的非递归遍历算法都是依靠辅助栈来实现的,而层次遍历是通过循环队列实现的,下面依次给出操作原理
前序遍历(DLR)的非递归思想: 
先定义一个栈,把二叉树的根结点入栈
然后栈顶出栈,一出栈便输出出栈结点,出栈的同时判断它是否有左右孩子,要有则入栈.注:要是左右孩子都存在则先右孩子入栈,因为先进后出嘛
最后在栈不为空的条件下一直循环执行步骤2
中序遍历(LDR)的非递归思想 
先定义一个栈,把二叉树的根结点入栈
把从根结点出发的左孩子一路入栈
碰到第一个没有左孩子的结点则访问输出该结点(即栈顶出栈结点),并让它指向自己的右孩子
若上一步中该结点有右孩子,则以他的右孩子出发,它有左孩子则左孩子统统入栈,即重复步骤2,步骤3
若步骤3中的结点没有右孩子,只要栈不为空便栈顶出栈,接着继续做步骤3的判断
后序遍历(LRD)的非递归遍历思想 
和前序遍历的过程一样就四点不同的地方,其他都一样 
需要两个辅助栈实现
当栈1出栈顶点左右孩子都存在时,先左孩子入栈
栈1出栈结点立马压入栈2,并不像前序那样立马访问输出
最后统一让栈2一次性出栈,每个结点一出栈就访问输出
后序遍历能这么干的依据是:逆后序遍历就是把先序遍历中对左右子树的遍历顺序交换的结果
二叉树的层次遍历实现思想(和图的广度优先遍历思想一致)

先创建一个二叉树结点类型队列,根结点若不为空则入队
紧接着对头出队,马上访问输出出队结点的数据域,出队的同时判断该结点是否有左右孩子,有的话则统统入队,注:左右孩子都有时先左孩子入队,先进先出嘛
在队不为空的情况下循环执行以上两步

 

二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历。由于树的定义本身就是递归定义,因此採用递归的方法去实现树的三种遍历不仅easy理解并且代码非常简洁,而对于广度遍历来说,须要其他数据结构的支撑。比方堆了。

四种基本的遍历思想为:

前序遍历:根结点 —> 左子树 —> 右子树

中序遍历:左子树—> 根结点 —> 右子树

后序遍历:左子树 —> 右子树 —> 根结点

层次遍历:仅仅需按层次遍历就可以

比如。求以下二叉树的各种遍历
 

         1
        /  \
       2    3
     / \   / 
   4   5  6
	  / \
	 7   8

前序遍历:1 2 4 5 7 8 3 6

中序遍历:4 2 7 5 8 1 3 6

后序遍历:4 7 8 5 2 6 3 1

层次遍历:1 2 3 4 5 6 7 8

二叉树的代码

public class TreeNode {
    public int val;
    public TreeNode left, right;

    public TreeNode(int val) {
        this.val = val;
        this.left = this.right = null;
    }
}

一、前序遍历

1)依据上文提到的遍历思路:根结点 —> 左子树 —> 右子树,非常easy写出递归版本号:

	public static void preOrderRecur(TreeNode head) {
        if (head == null) {
            return;
        }
        System.out.print(head.val + " ");

        preOrderRecur(head.left);
        preOrderRecur(head.right);
    }

2)如今讨论非递归的版本号:
依据前序遍历的顺序,优先訪问根结点。然后在訪问左子树和右子树。所以。对于随意结点node。第一部分即直接訪问之,之后在推断左子树是否为空,不为空时即反复上面的步骤,直到其为空。若为空。则须要訪问右子树。注意。在訪问过左孩子之后。须要反过来訪问其右孩子。所以,须要栈这样的数据结构的支持。对于随意一个结点node,详细过程例如以下:

a)訪问之,并把结点node入栈。当前结点置为左孩子;

b)推断结点node是否为空,若为空。则取出栈顶结点并出栈,将右孩子置为当前结点;否则反复a)步直到当前结点为空或者栈为空(能够发现栈中的结点就是为了訪问右孩子才存储的)

代码例如以下:
 

	//先序遍历的非递归的写法,反着它的顺序写
    // 1.先放中节点
    // 2.有右节点放右节点
    // 3.有左节点放左节点
    public static void preOrderUnRecur(TreeNode head) {
        if (head == null) {
            return;
        }
        System.out.print("pre-order: ");

        Stack<TreeNode> stack = new Stack<>();
        stack.add(head);
        while (!stack.isEmpty()) {
            TreeNode pop = stack.pop();
            System.out.print(pop.val + " ");
            if (pop.right != null) {
                stack.add(pop.right);
            }
            if (pop.left != null) {
                stack.add(pop.left);
            }
        }
    }

二、中序遍历
1)依据上文提到的遍历思路:左子树 —> 根结点 —> 右子树,非常easy写出递归版本号:

	public static void inOrderRecur(TreeNode head) {
        if (head == null) {
            return;
        }
        inOrderRecur(head.left);
        System.out.print(head.val + " ");
        inOrderRecur(head.right);
    }

2)非递归实现,有了上面前序的解释,中序也就比較简单了。同样的道理。仅仅只是訪问的顺序移到出栈时。代码例如以下:

	//中序遍历的非递归的写法,
    // 1.左节点不为null则压入左节点
    // 2.左节点为null时,pop并打印,左节点
    // 3.在往右,右节点为null时,pop并打印
    // 4.右节点不为null时,压入右节点
    //还是背下来吧
    public static void inOrderUnRecur(TreeNode head) {
        if (head == null) {
            return;
        }
        System.out.print("in-order: ");

        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty() || head != null) {
            if (head != null) {
                stack.add(head);
                head = head.left;
            } else {
                head = stack.pop();
                System.out.print(head.val + " ");
                head = head.right;
            }
        }
    }

三、后序遍历

1)依据上文提到的遍历思路:左子树 —> 右子树 —> 根结点。非常easy写出递归版本号:

	public static void posOrderRecur(TreeNode head) {
        if (head == null) {
            return;
        }
        posOrderRecur(head.left);
        posOrderRecur(head.right);
        System.out.print(head.val + " ");
    }

2)非递归实现

	//和前序遍历一样的只不过是使用了两个栈
    //在前序遍历的时候将 中 右 左 节点压栈
    //在原来是打印的地方不打印,将本该打印的节点压到第二个栈中
    //这样第二个栈的出栈顺序就是 右 左 中了
    public static void posOrderUnRecur1(TreeNode head) {
        if (head == null) {
            return;
        }
        System.out.print("pos-order: ");
        Stack<TreeNode> stack = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        stack.add(head);
        while (!stack.isEmpty()) {
            head = stack.pop();
            //在这里不打印,这里我们放到第二个栈中去
            stack2.add(head);
            if (head.left != null) {
                stack.add(head.left);
            }
            if (head.right != null) {
                stack.add(head.right);
            }
        }
        while (!stack2.isEmpty()) {
            System.out.print(stack2.pop().val + " ");
        }
    }

四、层次遍历

层次遍历的代码比較简单。仅仅须要一个队列就可以。先在队列中增加根结点。之后对于随意一个结点来说。在其出队列的时候,訪问之。同一时候假设左孩子和右孩子有不为空的。入队列。代码例如以下:

	//层次遍历
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            TreeNode poll = queue.poll();
            result.add(poll.val);
            if (poll.left != null) {
                queue.add(poll.left);
            }
            if (poll.left != null) {
                queue.add(poll.right);
            }
        }
        return result;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值