把二叉树打印成多行分析

20 篇文章 0 订阅
7 篇文章 0 订阅

题目依旧来自《剑指Offer》和牛客网在线编程。

【题目】从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

import java.util.ArrayList;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
    
    }
    
}

【分析1】通过观察题目最后要求的返回值类型,我们可以初步判断我们需要实现将二叉树的每一层保存到一个集合中,然后再把每一层的集合放到另外一个集合中返回。

【分析2】将二叉树的每一层放到一个集合中,这里很明显需要用到二叉树的层次遍历。二叉树的层次遍历主要思想是:使用一个循环队列,首先将二叉树的根结点入队。接下来 循环进行以下步骤,循环结束的条件为队列为空:将队头结点出队访问;如果队头结点有左子树,则将该结点的左子树根结点入队;如果队头结点有右子树,则将该结点的右子树根结点入队。

【分析3】在该问题中,要求我们将二叉树的每一层分别放到一个集合中保存,这就要求我们在层次遍历的时候加入一定的判断条件来判断这一层的遍历何时开始,何时结束。

【分析4】当时我的第一做法是使用了两个指针q,t 。q指针指向的是该层的最后一个结点,t指针指向的是当前队头出队访问的结点。那么也就是说如果q和t指向了同一个结点,说明此时遍历到了该层的最后一个结点,遍历完成后需要将保存当前层的集合加入到最后的各行集合中。然后再改变q的指向,使其指向下一层的截止结点。

【分析5】上面的做法虽然可行,但是毕竟要耗费两个指针。一种更好一点的方法是通过全局计数器的形式来实现判断该层何时遍历结束。具体想法为:在全局设置一个计数器,初始值为0。一旦有结点入队,则计数器加1,一旦有结点出队,则计数器减1。这样一来,一旦在队列不为空的判断中获取到了计数器为0的情况,则说明该层遍历结束。

【代码】下面给出分析4中使用两个指针的代码

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

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer> > res = new ArrayList<>();
        LinkedList<TreeNode> queen = new LinkedList<>();
        if(pRoot == null)
            return res;
        queen.add(pRoot);
        TreeNode q = pRoot;
        ArrayList<Integer> list = new ArrayList<>();
        while(queen.size() > 0){
            TreeNode t = queen.get(0);
            list.add(t.val);
            queen.remove(0);
            if(t == q){
                res.add(list);
                list = new ArrayList<>();
            }
            if(t.left != null && t.right == null ){
                queen.add(t.left);
                if(t == q)
                    q = t.left;
            }else if(t.left == null && t.right != null ){
                queen.add(t.right);
                if(t == q)
                    q = t.right;
            }else if(t.left != null && t.right != null){
                queen.add(t.left);
                queen.add(t.right);
                if(t == q)
                    q = t.right;
            }
        }
        return res;
    }
    
}

【小心有坑】这里面有一个小小的陷阱,就是将保存每一层的数据集合加入到需要返回的集合中时。这里需要两个集合,分别为list存储了每一层的结点值,另一个集合为res为存放每一层结点集合的集合。由于在上面代码的while循环中每一次循环只能遍历一个结点,因此list集合和res集合都要在循环外定义声明。当出队一个结点后,我们就要将该结点值加入到list集合中这很好理解,当访问完一层后,需要将list集合加入到res集合中,然后再将list集合中的数据清除。但是如果我们在清除list中的数据时使用的是list.clear();操作的话,那么最后我们得到的res中的结果均为空。

【为什么】其实这也很好理解,res和list均为引用,假设二叉树有4层,用上面的思想存储的话,则res集合中应该是[list,list,list,list]的形式。因此每次调用list.clear();操作的话最后的结果均为空。res集合中正确的存储应该是[list1,list2,list3,list4]而是四个不同的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值