数据结构和算法 - 二叉树的层次遍历和前中后序遍历

1 二叉树的定义

二叉树顾名思义:每一个父亲节点都至多含有两个子节点的树结构叫做二叉树。
二叉树程序定义可以设置为如下所示:
class TreeNode{
		private int data;
		private TreeNode left;
		private TreeNode right;
}
有了树节点的定义方式,我们就可以对整个树进行查询等操作。以下图二叉树为例讲解层次遍历和递归遍历方法。

二叉树

	在二叉树的结构中由于每一个节点都保存了本节点的数据信息(private int data)以及左右孩子(TreeNode left, right;)的位置信息,
	因此我们可以完美的使用递归函数来依次访问树中的每一个节点。
	递归函数的3要素:函数、边界条件和递推公式,递归函数形式有3个方面:函数名、返回值和参数列表。
1. 边界条件是结束递归循环的条件
2. 递推公式为函数体内部产生循环的主要部分,在树结构的访问中用来递归调用自身,完成整个树结构的遍历
// 前序遍历算法
    public static void recurTreeFront(TreeNode root){
        if(root == null)
            return;
        System.out.print(root.getData() + " ");
        recurTreeFront(root.getLeft());
        recurTreeFront(root.getRight());
    }
   // 中序遍历算法
       public static void recurTreeMiddle(TreeNode root){
        if(root == null)
            return;
        recurTreeMiddle(root.getLeft());
        System.out.print(root.getData() + " ");
        recurTreeMiddle(root.getRig    public static void recurTreeBehind(TreeNode root){
        if(root == null)
            return;
        recurTreeBehind(root.getLeft());
        recurTreeBehind(root.getRight());
        System.out.printf(root.getData() + " ");;
    }
   // 后续遍历算法
在上述代码过程中,可以发现如下几个特点:
	1. 在不需要返回树节点元素的时候,我们可以直接将函数定义为void,此时的输出就利用System.out.print(某节点存储的数据)来表示
	2. 前中后序遍历算法的次序区别在输出,System.out.println(root.getData())上,如果在某个节点上先输出,在递归调用左右节点则是前序遍历算法
	为什么会出现序号2所示的情况?递归函数和循环类似,要分析每一层递归调用或者循环体需要执行的步骤。
递归在一层调用(也就是访问某个节点)时,以root根节点为例,如果是前序遍历,此时进入函数体,先会检查是否为空,如果为空返回到上一层函数体,否则就会向下执行。
此时由于是中序遍历算法,先打印输出当前节点存储的数据data,然后在递归调用左右子树。

二叉树的层次遍历算法

1. 顺序输出每一层的元素

层次遍历算法属于树结构的广度优先算法,前中后序遍历算法属于深度优先算法。
因此在此处我们需要设计另外一种遍历算法区别与深度优先算法。
由于树节点的定义(class TreeNode),我们只知道当前节点和值和该节点的左右两个子节点的位置信息。
此时我们需要先访问父节点才能访问子节点,整体的访问顺序是从根节点出发向下访问。
根据此,我们应该如何对树结构进行层次遍历?
**解决方案**
我们可以想到队列这个数据结构的特点是FIFO,即先进先出,我们可以通过将root节点先压入队列,然后顺序的将其左右子节点依次压入队列。
最后将数据依次出队,得到的结果就是按照每一层的顺序依次输出的。

队列实现方式

代码实现

    public static LinkedList<TreeNode> simpleLevelOrder(TreeNode root){
        if(root == null)
            return new LinkedList<TreeNode>();
        Queue<TreeNode> queue = new LinkedList<>();
        LinkedList<TreeNode> list = new LinkedList<>();
        queue.add(root);
        // 如何代表层次遍历的结束?当栈区不为空的时候就添加元素
        while (!(queue.isEmpty())){
            // 将队列头部元素的子元素添加至队列!
            if(queue.peek().getLeft() != null)
                queue.add(queue.peek().getLeft());
            if(queue.peek().getRight() != null)
                queue.add(queue.peek().getRight());
            // 将队列头部元素压入到list中用于函数返回值
            list.add(queue.poll());
        }
        return list;
    }
此时含有一个小问题,利用此函数输出的数据其是一个列表,如何将每一层的数据保存的单独的列表中呢?
此问题的核心是如何得知每一层的节点数量。
我们可以在函数中定义size整形变量。如果树不为空的时候size初始值为1!
以根节点为例,上述代码中,root节点压入到queue以后会执行3个操作:
	1. 将左孩子节点压入到队列中
	2. 将右孩子节点压入到队列中
	3. 将root根节点poll出队列,用返回列表list接收。
在poll出去以后此时queue队列内只剩下了2个root的子节点,也就相应的变成了第二层的数据!
此时size的值就应该由于root的poll出去从1变成0,然后由于队列中两个子节点的数据由第一层的0变成第二层的2.。
同理,当size 减到0的时候,也就是第二层数据全部poll出去的时候,由于两个节点分别调用了压入左右子节点,所以queue的数据就变成了第3层的数据!(如上图所示)

代码实现

public static LinkedList<LinkedList<TreeNode>> simpleLevelLayer(TreeNode root){
        if(root == null)
            return new LinkedList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        LinkedList<LinkedList<TreeNode>> list = new LinkedList<>();
        LinkedList<TreeNode> layer = new LinkedList<>();
        // 如果存在根节点,那么根节点为1
        int size = 1;
        while (!(queue.isEmpty())){
            if(queue.peek().getLeft() != null)
                queue.add(queue.peek().getLeft());
            if(queue.peek().getRight() != null)
                queue.add((queue.peek().getRight()));
            // layer接收每一层的元素
            // 此时此处的layer应该是变化的,否则,每一层的数据都会添加到此layer上面!
            layer.add(queue.poll());
            if(--size == 0){
                list.add(layer);
                // list添加了这部分内存空间以后就可以让layer继续开辟新的内存空间!!!!
                layer = new LinkedList<>();
                size = queue.size();
            }
        }
        return list;
    }
整体代码实现
// 整体的代码实现为

import java.util.LinkedList;
import java.util.Queue;

public class TreeTest {
    public static void main(String[] args) {
        TreeNode node3 = new TreeNode(3);
        TreeNode node9 = new TreeNode(9);
        TreeNode node20 = new TreeNode(20);
        TreeNode node8 = new TreeNode(8);
        TreeNode node13 = new TreeNode(13);
        TreeNode node15 = new TreeNode(15);
        TreeNode node17 = new TreeNode(17);
        node3.setLeft(node9);
        node3.setRight(node20);
        node9.setLeft(node8);
        node9.setRight(node13);
        node20.setLeft(node15);
        node20.setRight(node17);
        LinkedList<TreeNode> list = simpleLevelOrder(node3);
        for(TreeNode node : list){
            System.out.print(node.getData() + " ");
        }
        System.out.println();
        LinkedList<LinkedList<TreeNode>> listLayer = simpleLevelLayer(node3);
        for(LinkedList<TreeNode> list1 : listLayer){
            System.out.print("[");
            for(TreeNode node : list1){
                System.out.print(node.getData() + " ");
            }
            System.out.print("],");
            System.out.println();
        }

//        System.out.println("=============recurFront========");
//        recurTreeFront(node3);
//        System.out.println();
//        System.out.println("=============recurMiddle========");
//        recurTreeMiddle(node3);
//        System.out.println();
//        System.out.println("=============recurBehind========");
//        recurTreeBehid(node3);

    }
    //
    public static LinkedList<TreeNode> simpleLevelOrder(TreeNode root){
        if(root == null)
            return new LinkedList<TreeNode>();
        Queue<TreeNode> queue = new LinkedList<>();
        LinkedList<TreeNode> list = new LinkedList<>();
        queue.add(root);
        // 如何代表层次遍历的结束?当栈区不为空的时候就添加元素
        while (!(queue.isEmpty())){
            // 将队列头部元素的子元素添加至队列!
            if(queue.peek().getLeft() != null)
                queue.add(queue.peek().getLeft());
            if(queue.peek().getRight() != null)
                queue.add(queue.peek().getRight());
            list.add(queue.poll());
        }
        return list;
    }
    public static void recurTreeFront(TreeNode root){
        if(root == null)
            return;
            // 前序遍历算法
        System.out.print(root.getData() + " ");
        recurTreeFront(root.getLeft());
        recurTreeFront(root.getRight());
    }
    public static void recurTreeMiddle(TreeNode root){
        if(root == null)
            return;
        recurTreeMiddle(root.getLeft());
        System.out.print(root.getData() + " ");
        recurTreeMiddle(root.getRight());
    }
    public static void recurTreeBehind(TreeNode root){
        if(root == null)
            return;
        recurTreeBehind(root.getLeft());
        recurTreeBehind(root.getRight());
        System.out.printf(root.getData() + " ");;
    }
    public static LinkedList<LinkedList<TreeNode>> simpleLevelLayer(TreeNode root){
        if(root == null)
            return new LinkedList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        LinkedList<LinkedList<TreeNode>> list = new LinkedList<>();
        LinkedList<TreeNode> layer = new LinkedList<>();
        // 如果存在根节点,那么根节点为1
        int size = 1;
        while (!(queue.isEmpty())){
            if(queue.peek().getLeft() != null)
                queue.add(queue.peek().getLeft());
            if(queue.peek().getRight() != null)
                queue.add((queue.peek().getRight()));
            // layer接收每一层的元素
            layer.add(queue.poll());
            if(--size == 0){
                list.add(layer);
                // list添加了这部分内存空间以后就可以让layer继续开辟新的内存空间
                layer = new LinkedList<>();
                size = queue.size();
            }
        }
        return list;
    }
}

class TreeNode{
    private int data;
    private TreeNode left;
    private TreeNode right;

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

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public TreeNode getLeft() {
        return left;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public TreeNode getRight() {
        return right;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }
}

由于还没学会树的创建,因此在主函数中用了大篇幅定义树结构,如有错误和改进的地方,还望众位大佬批评指正。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值