给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
示例:
二叉树:[3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层序遍历结果:
[
[3],
[9,20],
[15,7]
]
方法一:广度优先搜索(BFS)
思路和算法:
我们可以想到最朴素的方法是用一个二元组(node,level)来表示状态,它表示某个节点和它所在的层数,每个新进对列的节点level值都是父亲节点的level值加1。最后根据每个点的level对点进行分类,分类的时候我们可以采用哈希表,维护一个以level为键,对应节点值组成数组的值,广度优先搜索结束以后按键level从小到大取出所有值,组成答案并返回即可。
考虑如何优化空间开销,如何不用哈希映射,并且只用一个node表示状态,实现这个功能呢?
我们可以用一种方法修改广度优先搜索。
- 首先根元素入队列
- 当队列不为空时
1)求当前队列的长度Si
2)依次从队列中取出Si个元素进行拓展,然后进行下一次迭代
它和普通广度优先搜索的区别在于,普通广度优先搜索每次只取一个元素拓展,而这里每次取出Si个元素。上述过程中的第i次迭代就得到了二叉树第i层的Si个元素。
为什么这么做是对的呢?我们观察这个算法,可以归纳出这样的循环不变式:第 i次迭代前,队列中的所有元素就是第 i 层的所有元素,并且按照从左向右的顺序排列。证明它的三条性质(你也可以把它理解成数学归纳法):
- 初始化:i = 1 的时候,队列里面只有
root
,是唯一的层数为 1 的元素,因为只有一个元素,所以也显然满足「从左向右排列」; -
保持:如果 i = ki=k 时性质成立,即第 k 轮中出队Sk的元素是第 k 层的所有元素,并且顺序从左到右。因为对树进行广度优先搜索的时候由低 k层的点拓展出的点一定也只能是 k+1 层的点,并且k+1 层的点只能由第 k 层的点拓展到,所以由这Sk 个点能拓展到下一层所有的
S(k+1)个点。又因为队列的先进先出(FIFO)特性,既然第 k 层的点的出队顺序是从左向右,那么第 k+1 层也一定是从左向右。至此,我们已经可以通过数学归纳法证明循环不变式的正确性。 - 终止:因为该循环不变式是正确的,所以按照这个方法迭代之后每次迭代得到的也就是当前层的层次遍历结果。至此,我们证明了算法是正确的。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList();
if(root == null){
return ret;//若为空
}
//创建一个队列,添加每层的元素
Queue<TreeNode> queue = new LinkedList();
queue.offer(root);//将根节点加入
while(!queue.isEmpty()){//队列不为空
List<Integer> list = new ArrayList();
int currentSize = queue.size();//当前队列的长度,每层的节点数
for(int i = 0;i<currentSize;i++){//遍历每层的节点,将节点值加入list,同时进行下一层的迭代。
TreeNode node = queue.poll();
list.add(node.val);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
ret.add(list);
}
return ret;
}
}
//时间复杂度:O(N) 每个节点都要遍历一次
//空间复杂度:O(N)
方法二:递归
class Solution {
List<List<Integer>> levels = new ArrayList();//需要返回的list
public void helper(TreeNode node,int level){//辅助的递归函数
if(levels.size() == level){
levels.add(new ArrayList<Integer>());//每一次新开一个数组,相同层,放入同一个数组
}
levels.get(level).add(node.val);
if(node.left != null){
helper(node.left,level+1);
}
if(node.right != null){
helper(node.right,level+1);
}
}
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null) return levels;
helper(root,0);
return levels;
}
}
//时间复杂度:O(N)
//空间复杂度:O(N)