给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
二、思路
迭代-队列
由题意知,需要对数进行层次(序)遍历,可引入一个队列,维护树节点的访问顺序。开始时插入根节点入队列中。只要队列不为空,便取出一个队列头进行分析,先对其进行需要的操作,再将其子节点入队。本题中的“所需操作”即将节点的值插入一个二维数组中。
本题难点在于如何确定在队列中到底哪些节点属于同一层,从而将相同层的节点的值放入二维数组的同一行中。
这里可以利用队列的size()方法,在开始访问第i层的第一个节点之前,队列的size()便是第i层的节点数目。因此,可以在取出每层的第一个节点之前先套用一个for循环,循环次数即为q.size()。
class Solution {
public:
vector> levelOrder(TreeNode* root) {
vector> ans;//返回结果的二维数组
if (!root)
return ans;//空节点的退化情况
queue q;//引入一个队列,维护层次遍历(广度优先搜索)顶点访问次序
q.push(root);//根节点入队
while (!q.empty())
{
vector levelNum;//声明一位数组用于存储当前层的值
int n = q.size();
for (int i = 0; i < n; i++)//当前层的节点个数即为q.size(),出了这个循环意味着进入下一层的遍历
{
TreeNode* t = q.front();//取出队首
q.pop();
levelNum.push_back(t->val);
if (t->left)
q.push(t->left);
if (t->right)
q.push(t->right);
}
ans.push_back(levelNum);
}
return ans;
}
};
注意:for循环的写法细节:
int n = q.size();
for (int i = 0; i < n; i++)
这里先声明了n来存储q.size(),然后再在for循环中将i for (int i = 0; i < q.size(); i++) 呢? 因为在for循环中由于父节点的取出和子节点的插入,q.size()是动态变化的。而我们想要的循环次数是进行遍历取出该层第一个节点之前的size(),因此需要先用一个变量n来存储先前的q.size()。否则,如下图所示,可以细细品味。 我们可以想到最朴素的方法是用一个二元组 (node, level) 来表示状态,它表示某个节点和它所在的层数,每个新进队列的节点的 level 值都是父亲节点的 level 值加一。最后根据每个点的 level 对点进行分类,分类的时候我们可以利用哈希表,维护一个以 level 为键,对应节点值组成的数组为值,广度优先搜索结束以后按键 level 从小到大取出所有值,组成答案返回即可。 考虑如何优化空间开销:如何不用哈希映射,并且只用一个变量 node 表示状态,实现这个功能呢? 我们可以用一种巧妙的方法修改广度优先搜索: 求当前队列的长度si 依次从队列中取si个元素进行拓展,然后进入下一次迭代 它和普通广度优先搜索的区别在于,普通广度优先搜索每次只取一个元素拓展,而这里每次取si个元素。在上述过程中的第 i 次迭代就得到了二叉树的第 i 层的si个元素。 为什么这么做是对的呢?我们观察这个算法,可以归纳出这样的循环不变式:第 i 次迭代前,队列中的所有元素就是第 i 层的所有元素,并且按照从左向右的顺序排列。证明它的三条性质(你也可以把它理解成数学归纳法): 复杂度分析 记树上所有节点的个数为 n。广度优先搜索
class Solution {
public:
vector



