同学们,我把《剑指Offer》中所有“树”标签的习题及思路整理出来了,对照学习效果更佳:
题目分析:层序遍历、广度优先(BFS)一般都是借助队列的!!!
这道题建议参考二叉树的深度,其中的第二种解法——层序遍历,用队列的思想巧妙地实现。
不同于上一道题从上到下打印二叉树,这道题要求每一行输出一个数组,而不是一个一维数组,需要变成二维数组
所以问题的难点就来了:
怎么确定哪些元素是来自同一行呢??
接下来介绍两种方法,能够很巧妙地确定元素的行所属:
方法一:两个辅助队列
这个方法就是二叉树的深度中使用的方法,借助两个队列,两个List
集合,能够很好地完成任务,但是效率略低。
算法步骤:
- 判断根节点是否为空(常规判断,注意按题意返回值)
- 创建辅助队列(
queue
和help
)和集合(res
和list
) - 进入循环,循环条件是
queue
不为空 - 在增强for中,节点出
queue
,值加入list
- 判断是否有左右子节点,再加入
help
- 当for循环结束,说明一行结束了,此时把
list
加入res
- 将
help
赋值给queue
,同时等待下一轮循环help
重置 - 因此直到所有点遍历完,
queue
才会空
代码如下:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root==null) return new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
List<Integer> list=null;
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
Queue<TreeNode> help=null;
while(!queue.isEmpty()) {
help=new LinkedList<>();
list=new ArrayList<>();
for(TreeNode node : queue) {
//加入大集合
list.add(node.val);
if(node.left != null) help.add(node.left);
if(node.right != null) help.add(node.right);
}
res.add(list);
queue=help;
}
return res;
}
}
方法二:一个辅助队列+ 循环
看了大神的题解,这方法真实太巧妙了!!!!!膜拜膜拜
最巧妙地点在于,通过for循环的次数,控制了每一行的元素同一轮出队列
算法步骤和上面基本类似,只是控制每行的思路不同:
- 不需要第二个
help
队列,而是每次都加入queue
- 循环前先获取
queue
的长度,在循环中再添加,因此,后添加的并不会被遍历,只能等到下一轮(就这里最巧妙!!!) - 一轮结束,赋值给
res
代码如下:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null) {
return new ArrayList<>();
}
List<List<Integer>> res=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()) {
List<Integer> list=new ArrayList<>();
for(int i=queue.size();i>0;i--) {
TreeNode node=queue.poll();
list.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(list);
}
return res;
}
}
复杂度分析:
- 时间复杂度:O(N),同样道理,遍历树中所有节点
- 空间复杂度:O(N),需要队列来辅助