原题链接:https://leetcode.com/problems/binary-tree-level-order-traversal/
1. 题目介绍
Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).
给出一个二叉树,按照逐层遍历的顺序扫描所有的节点,返回扫描的节点的value值。
For example:
Given binary tree [3,9,20,null,null,15,7],
return its level order traversal as:
2. 解题思路
方法1 BFS+标记
这题是考察广度优先搜索的经典题目。广度优先搜索的使用和队列是密不可分的。每次都会把队列最前面的节点的左右子节点放到队列的最后面,然后再弹出最前面的节点。
本题的另一个难点是如何分层。思路是,在队列中使用null作为标志来分开不同的层。
上图的例子应该以
3,null,9,20,null,15,7,null的顺序依次放入队列。
一开始队列中只有 3,null 。当弹出3后,队列中变为了 null,9,20。此时队列最前面是null,在这种情况下就需要在队列的后面补一个null。于是当弹出null,9,20中的null后,队列应该变为9,20,null,于是就可以用null分割不同的层了。
这个方法使用递归和循环都可以实现。
实现代码-递归
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null){
return new ArrayList<>();
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
queue.add(null);
//层与层之间用null相互隔开
List<List<Integer>> ans = new ArrayList<>();
List<Integer> l = new ArrayList<>();
BFS(queue,l,ans);
return ans;
}
public void BFS(Queue<TreeNode> q, List<Integer> l, List<List<Integer>> ans ){
if (q.isEmpty() == true) {
return;
}
TreeNode t = q.remove();//取出队列最前方元素并将其从队列中移除
if(t == null){
//标志着上面一层的子节点已经全部进入队列,需要添加null作为分割标志
if(q.isEmpty() == false){
q.add(null);
}
//此处不能直接插入l,需要插入l的深拷贝
List<Integer> temp = new ArrayList<>(l);
ans.add(temp);
l.clear();
}else{
//将左右子节点添加入队列
l.add(t.val);
if (t.left != null) {
q.add(t.left);
}
if (t.right != null) {
q.add(t.right);
}
}
BFS(q,l,ans);
}
}
实现代码-循环
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null){
return new ArrayList<>();
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
queue.add(null);
//层与层之间用null相互隔开
List<List<Integer>> ans = new ArrayList<>();
List<Integer> l = new ArrayList<>();
while(queue.isEmpty() == false){
TreeNode t = queue.remove();//取出队列最前方元素并将其从队列中移除
if(t == null){
//标志着上面一层的子节点已经全部进入队列,需要添加null作为分割标志
if(queue.isEmpty() == false){
queue.add(null);
}
//此处不能直接插入l,需要插入l的深拷贝
List<Integer> temp = new ArrayList<>(l);
ans.add(temp);
l.clear();
}else{
//将左右子节点添加入队列
l.add(t.val);
if (t.left != null) {
queue.add(t.left);
}
if (t.right != null) {
queue.add(t.right);
}
}
}
return ans;
}
}
方法2 BFS+计数
该方法来自于 https://www.cnblogs.com/love-yh/p/6961774.html 的方法二,有兴趣的读者可以直接去看原文。
首先有一个规律:遍历完一层以后,队列中节点的个数就是二叉树下一层的节点数。
比如遍历完3 后,队列中就剩下9 和20 了,队列的长度就是第二层的节点数目。
于是,可以使用Count计算每层已经遍历的节点个数,用Count和队列一开始的长度进行比较来判断是否遍历完了某一层全部的节点。如果Count没有超过队列的长度,就继续遍历。
实现代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if (root == null) {
return new ArrayList<>();
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
List<List<Integer>> ans = new ArrayList<>();
int Count = 0;
int length =0;
while(queue.isEmpty() == false){
Count = 0;
length = queue.size();
List<Integer> l = new ArrayList<>();
while(Count < length){
TreeNode t = queue.remove();
Count ++;
l.add(t.val);
if (t.left != null) {
queue.add(t.left);
}
if (t.right != null) {
queue.add(t.right);
}
}
ans.add(l);
}
return ans;
}
}
方法3 DFS
这个方法是所有提交代码中解题速度最快的代码,执行速度是0ms,竟然是用DFS做的。代码来自LeetCode。
使用一个标志 height 记录遍历的层数。同时用 res 存储结果。
如果 res 中存储的元素个数等于已经遍历的层数,那么就需要为 res 增加一个元素用于存放第 height 层的节点val值。、
根据height,可以将深度优先遍历的节点放到正确的层里面。这就是这个方法的精妙所在。
具体实现可以看下面的代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
helper(root, res, 0);
return res;
}
public void helper(TreeNode node, List<List<Integer>> res, int height) {
if (node == null) return;
// start a new level
if (height == res.size()) {
List<Integer> newLevel = new ArrayList<Integer>();
res.add(newLevel);
}
res.get(height).add(node.val);
helper(node.left, res, height+1);
helper(node.right, res, height+1);
}
}