CPP:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
类定义
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root);
};
这里定义了一个名为Solution的类,其中包含一个公开的成员函数levelOrder,该函数接收一个指向二叉树根节点的指针root,并返回一个二维向量,该向量包含了二叉树层序遍历的结果。
成员函数实现
vector<vector<int>> levelOrder(TreeNode* root) {
// 使用队列进行层序遍历
queue<TreeNode*> que;
// 如果根节点不为空,则将其加入队列
if (root != NULL) que.push(root);
// 初始化结果向量
vector<vector<int>> result;
// 当队列不为空时,继续遍历
while (!que.empty()) {
// 获取当前层的节点数量
int size = que.size();
// 初始化一个向量,用于存储当前层的节点值
vector<int> vec;
// 遍历当前层的所有节点
// 注意:这里使用size而不是que.size(),因为que.size()在循环中会变化
for (int i = 0; i < size; i++) {
// 从队列中取出一个节点
TreeNode* node = que.front();
que.pop();
// 将节点值加入当前层的向量
vec.push_back(node->val);
// 如果节点有左子节点,则将其加入队列
if (node->left) que.push(node->left);
// 如果节点有右子节点,则将其加入队列
if (node->right) que.push(node->right);
}
// 将当前层的节点值向量加入结果向量
result.push_back(vec);
}
// 返回结果
return result;
关键点
-
队列的使用:队列是一种先进先出(FIFO)的数据结构,非常适合用于层序遍历。在遍历过程中,我们首先将根节点(如果非空)加入队列,然后不断从队列中取出节点,并访问其左右子节点(如果存在),将子节点加入队列。
-
当前层节点数量的确定:在遍历每一层之前,我们首先获取队列的大小(即当前层的节点数量),然后基于这个大小进行遍历。这是因为在遍历过程中,队列的大小会随着子节点的加入而增加,如果我们在循环中直接使用
que.size(),那么可能会导致同一层的节点被遍历多次。 -
结果向量的构建:我们使用一个二维向量
result来存储层序遍历的结果。对于每一层,我们都先初始化一个一维向量vec,用于存储当前层的节点值,然后将这个向量加入result。 -
空节点的处理:在代码中,我们没有显式地处理空节点的情况,因为空节点不会被加入队列。只有当节点存在时,我们才会将其加入队列,并访问其左右子节点。
-
返回值:函数最终返回
result,即包含了二叉树层序遍历结果的二维向量。
C代码:
public IList<IList<int>> LevelOrder(TreeNode root)
{
var res = new List<IList<int>>();
var que = new Queue<TreeNode>();
if (root == null) return res;
que.Enqueue(root);
while (que.Count != 0)
{
var size = que.Count;
var vec = new List<int>();
for (int i = 0; i < size; i++)
{
var cur = que.Dequeue();
vec.Add(cur.val);
if (cur.left != null) que.Enqueue(cur.left);
if (cur.right != null) que.Enqueue(cur.right);
}
res.Add(vec);
}
return res;
}
以下是逐帧扣的解析
该方法实现了二叉树的层序遍历,即广度优先遍历,并返回一个二维列,其中每个内部列表代表树的一层节点的值。
方法签名
public IList<IList<int>> LevelOrder(TreeNode root)
- 返回类型-->一个嵌套列表,外层列表代表不同的层,内层列表包含该层所有节点的值。
- 参数-->
TreeNode root,表示二叉树的根节点。
方法体
- 初始化结果列表和队列:
- 创建一个空列表
res,用于存储每层的节点值(每层的节点值本身也会作为一个列表存储)。var res = new List<IList<int>>(); - 创建一个队列
que,用于存储待处理的节点。队列元素先入先出,适合实现层序遍历。var que = new Queue<TreeNode>();
- 创建一个空列表
- 处理空树情况:如果根节点
root为空,则直接返回空的res列表,因为空树没有节点可以遍历。 - 将根节点加入队列:
- 将根节点加入队列,准备开始遍历。
que.Enqueue(root);
- 将根节点加入队列,准备开始遍历。
- 层序遍历循环:使用
while循环,只要队列不为空就继续遍历。队列不为空意味着还有节点没有处理。 - 处理当前层的节点:
- 在遍历每一层之前,首先获取队列的当前大小
size,这是因为我们需要在不添加新节点到队列的情况下遍历当前层的所有节点。队列的大小在遍历过程中会因为新节点的加入而改变,但size保持了当前层的节点数。 - 创建一个新列表
vec,用于存储当前层的节点值。 - 使用
for循环遍历当前层的所有节点(循环次数为size)。在每次迭代中:- 从队列中取出当前节点
cur(que.Dequeue())。 - 将当前节点的值添加到
vec列表中。 - 如果当前节点有左子节点,则将左子节点加入队列。
- 如果当前节点有右子节点,则将右子节点加入队列。
- 从队列中取出当前节点
- 在遍历每一层之前,首先获取队列的当前大小
- 将当前层的节点值列表添加到结果列表中:将
vec(包含当前层所有节点值的列表)添加到res列表中。 - 返回结果:当队列为空时,表示所有节点都已遍历完毕,返回
res列表作为层序遍历的结果。
Python代码:
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
queue = collections.deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
cur = queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
result.append(level)
return result
类定义
首先,定义了一个名为Solution的类,其中包含一个名为levelOrder的方法。这个方法接收一个二叉树的根节点root(类型为Optional[TreeNode],表示根节点可以是TreeNode类型或者None),并返回一个二维列表(List[List[int]]),其中包含了二叉树层序遍历的结果。
方法实现
- 边界条件处理:
- 如果根节点
root为空,那么直接返回一个空列表[],因为空树没有节点可以遍历。
- 如果根节点
- 初始化队列和结果列表:
- 使用
collections.deque创建一个双端队列queue,并将根节点root加入队列中。这里虽然使用了双端队列,但只使用了它作为队列的功能(即只从一端添加和移除元素)。 - 初始化一个空列表
result,用于存储层序遍历的结果,每一层的结果将是一个包含节点值的列表。
- 使用
- 层序遍历:
- 使用一个
while循环来遍历队列,直到队列为空。 - 在循环内部,首先初始化一个空列表
level,用于存储当前层的节点值。 - 使用一个
for循环,循环次数为当前队列的长度(即当前层的节点数)。在每次循环中:- 从队列的左端弹出一个节点
cur(这是当前层的一个节点)。 - 将
cur的节点值添加到level列表中。 - 如果
cur有左子节点,则将左子节点加入队列中。 - 如果
cur有右子节点,则将右子节点加入队列中。
- 从队列的左端弹出一个节点
- 当完成当前层的所有节点处理后,将
level列表(即当前层的节点值列表)添加到result列表中。
- 使用一个
- 返回结果:
- 当队列为空时,所有层的节点都已经遍历完成,此时
result列表中包含了所有层的节点值列表。函数返回result列表作为层序遍历的结果。
- 当队列为空时,所有层的节点都已经遍历完成,此时
关键点
- 队列的使用:队列是实现层序遍历的关键数据结构,它保证了节点按照层次顺序被访问。
- 长度法:在遍历每一层时,通过
range(len(queue))来确定当前层的节点数,这种方法被称为“长度法”。它避免了在遍历过程中由于子节点的加入而导致的队列长度变化问题。 - 双端队列与队列:虽然这里使用了
collections.deque,但主要利用了其作为队列的功能。双端队列的特点是可以在两端进行添加和移除操作,但在这里只使用了其一端。
这段代码有效地实现了二叉树的层序遍历,并清晰地展示了队列在层序遍历中的应用。
总结
该方法通过队列实现了二叉树的层序遍历。在遍历过程中,首先处理当前层的所有节点,然后将它们的子节点加入队列以供后续处理。通过这种方式,能够按照从上到下、从左到右的顺序遍历二叉树的所有节点,并将每层的节点值作为一个列表存储在结果列表中。
1786

被折叠的 条评论
为什么被折叠?



