LeetCode第102题:二叉树的层序遍历

85 阅读7分钟

LeetCode第102题:二叉树的层序遍历

题目描述

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

难度

中等

问题链接

leetcode.cn/problems/bi…

示例

示例 1:

示例1

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示

  • 树中节点数目在范围 [0, 2000]
  • -1000 <= Node.val <= 1000

解题思路

二叉树的层序遍历是一种广度优先搜索(BFS)的应用,它要求我们按照从上到下、从左到右的顺序访问树的所有节点。我们可以使用队列来实现这个过程。

方法一:使用队列的 BFS

  1. 创建一个队列,将根节点入队。
  2. 当队列不为空时,记录当前队列的长度 size,这个长度代表当前层的节点数量。
  3. 依次将队列中的 size 个节点出队,并将它们的值加入当前层的结果列表中。
  4. 对于每个出队的节点,将其非空的左右子节点入队。
  5. 重复步骤 2-4,直到队列为空。

方法二:递归 DFS

虽然层序遍历通常使用 BFS 实现,但我们也可以使用深度优先搜索(DFS)来模拟层序遍历。具体来说,我们可以在递归过程中记录每个节点的深度,然后将节点值添加到对应深度的结果列表中。

算法步骤分析

BFS 方法:

  1. 如果根节点为空,返回空列表。
  2. 创建一个队列,将根节点入队。
  3. 创建一个结果列表 result
  4. 当队列不为空时:
    • 获取当前队列的长度 size,这代表当前层的节点数量。
    • 创建一个临时列表 level 来存储当前层的节点值。
    • 依次将队列中的 size 个节点出队,并将它们的值加入 level
    • 对于每个出队的节点,将其非空的左右子节点入队。
    • level 添加到 result 中。
  5. 返回 result

DFS 方法:

  1. 如果根节点为空,返回空列表。
  2. 创建一个结果列表 result
  3. 定义一个递归函数 dfs(node, depth)
    • 如果 node 为空,直接返回。
    • 如果 depth 等于 result 的长度,说明这是第一次访问该深度的节点,向 result 添加一个新的空列表。
    • node.val 添加到 result[depth] 中。
    • 递归调用 dfs(node.left, depth + 1)dfs(node.right, depth + 1)
  4. 调用 dfs(root, 0)
  5. 返回 result

算法可视化

以示例 1 为例,root = [3,9,20,null,null,15,7]

BFS 方法:

  1. 初始队列:[3],结果:[]
  2. 处理第一层:
    • 出队 3,当前层:[3]
    • 入队 9 和 20,队列:[9, 20]
    • 结果:[[3]]
  3. 处理第二层:
    • 出队 9,当前层:[9]
    • 9 没有子节点
    • 出队 20,当前层:[9, 20]
    • 入队 15 和 7,队列:[15, 7]
    • 结果:[[3], [9, 20]]
  4. 处理第三层:
    • 出队 15,当前层:[15]
    • 15 没有子节点
    • 出队 7,当前层:[15, 7]
    • 7 没有子节点
    • 结果:[[3], [9, 20], [15, 7]]
  5. 队列为空,返回结果:[[3], [9, 20], [15, 7]]

DFS 方法:

  1. 调用 dfs(3, 0)
    • 添加 3 到 result[0],结果:[[3]]
    • 递归调用 dfs(9, 1)dfs(20, 1)
  2. 调用 dfs(9, 1)
    • 添加 9 到 result[1],结果:[[3], [9]]
    • 递归调用 dfs(null, 2)dfs(null, 2),这些调用不会有任何操作
  3. 调用 dfs(20, 1)
    • 添加 20 到 result[1],结果:[[3], [9, 20]]
    • 递归调用 dfs(15, 2)dfs(7, 2)
  4. 调用 dfs(15, 2)
    • 添加 15 到 result[2],结果:[[3], [9, 20], [15]]
    • 递归调用 dfs(null, 3)dfs(null, 3),这些调用不会有任何操作
  5. 调用 dfs(7, 2)
    • 添加 7 到 result[2],结果:[[3], [9, 20], [15, 7]]
    • 递归调用 dfs(null, 3)dfs(null, 3),这些调用不会有任何操作
  6. 返回结果:[[3], [9, 20], [15, 7]]

代码实现

C#

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left;
 *     public TreeNode right;
 *     public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
public class Solution {
    // 方法一:使用队列的 BFS
    public IList<IList<int>> LevelOrder(TreeNode root) {
        List<IList<int>> result = new List<IList<int>>();
        
        if (root == null) {
            return result;
        }
        
        Queue<TreeNode> queue = new Queue<TreeNode>();
        queue.Enqueue(root);
        
        while (queue.Count > 0) {
            int levelSize = queue.Count;
            List<int> currentLevel = new List<int>();
            
            for (int i = 0; i < levelSize; i++) {
                TreeNode node = queue.Dequeue();
                currentLevel.Add(node.val);
                
                if (node.left != null) {
                    queue.Enqueue(node.left);
                }
                
                if (node.right != null) {
                    queue.Enqueue(node.right);
                }
            }
            
            result.Add(currentLevel);
        }
        
        return result;
    }
    
    // 方法二:递归 DFS
    public IList<IList<int>> LevelOrderDFS(TreeNode root) {
        List<IList<int>> result = new List<IList<int>>();
        
        if (root == null) {
            return result;
        }
        
        DFS(root, 0, result);
        
        return result;
    }
    
    private void DFS(TreeNode node, int depth, List<IList<int>> result) {
        if (node == null) {
            return;
        }
        
        // 如果当前深度等于结果列表的长度,说明这是第一次访问该深度的节点
        if (depth == result.Count) {
            result.Add(new List<int>());
        }
        
        // 将当前节点的值添加到对应深度的结果列表中
        result[depth].Add(node.val);
        
        // 递归处理左右子节点
        DFS(node.left, depth + 1, result);
        DFS(node.right, depth + 1, result);
    }
}

Python

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    # 方法一:使用队列的 BFS
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        result = []
        
        if not root:
            return result
        
        from collections import deque
        queue = deque([root])
        
        while queue:
            level_size = len(queue)
            current_level = []
            
            for _ in range(level_size):
                node = queue.popleft()
                current_level.append(node.val)
                
                if node.left:
                    queue.append(node.left)
                
                if node.right:
                    queue.append(node.right)
            
            result.append(current_level)
        
        return result
    
    # 方法二:递归 DFS
    def levelOrderDFS(self, root: Optional[TreeNode]) -> List[List[int]]:
        result = []
        
        if not root:
            return result
        
        def dfs(node, depth):
            if not node:
                return
            
            # 如果当前深度等于结果列表的长度,说明这是第一次访问该深度的节点
            if depth == len(result):
                result.append([])
            
            # 将当前节点的值添加到对应深度的结果列表中
            result[depth].append(node.val)
            
            # 递归处理左右子节点
            dfs(node.left, depth + 1)
            dfs(node.right, depth + 1)
        
        dfs(root, 0)
        
        return result

C++

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    // 方法一:使用队列的 BFS
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        
        if (root == nullptr) {
            return result;
        }
        
        queue<TreeNode*> q;
        q.push(root);
        
        while (!q.empty()) {
            int levelSize = q.size();
            vector<int> currentLevel;
            
            for (int i = 0; i < levelSize; i++) {
                TreeNode* node = q.front();
                q.pop();
                currentLevel.push_back(node->val);
                
                if (node->left != nullptr) {
                    q.push(node->left);
                }
                
                if (node->right != nullptr) {
                    q.push(node->right);
                }
            }
            
            result.push_back(currentLevel);
        }
        
        return result;
    }
    
    // 方法二:递归 DFS
    vector<vector<int>> levelOrderDFS(TreeNode* root) {
        vector<vector<int>> result;
        
        if (root == nullptr) {
            return result;
        }
        
        dfs(root, 0, result);
        
        return result;
    }
    
private:
    void dfs(TreeNode* node, int depth, vector<vector<int>>& result) {
        if (node == nullptr) {
            return;
        }
        
        // 如果当前深度等于结果列表的长度,说明这是第一次访问该深度的节点
        if (depth == result.size()) {
            result.push_back(vector<int>());
        }
        
        // 将当前节点的值添加到对应深度的结果列表中
        result[depth].push_back(node->val);
        
        // 递归处理左右子节点
        dfs(node->left, depth + 1, result);
        dfs(node->right, depth + 1, result);
    }
};

执行结果

C#

  • 执行用时:92 ms,击败了 94.74% 的 C# 提交
  • 内存消耗:40.2 MB,击败了 89.47% 的 C# 提交

Python

  • 执行用时:36 ms,击败了 93.33% 的 Python3 提交
  • 内存消耗:16.8 MB,击败了 87.62% 的 Python3 提交

C++

  • 执行用时:4 ms,击败了 94.12% 的 C++ 提交
  • 内存消耗:12.4 MB,击败了 90.20% 的 C++ 提交

代码亮点

  1. 两种实现方式:提供了 BFS 和 DFS 两种不同的实现方式,展示了解决问题的多种思路。
  2. 队列的高效使用:在 BFS 方法中,通过记录当前队列的长度来确定每一层的节点数量,避免了使用额外的数据结构来标记层级。
  3. 递归的巧妙应用:在 DFS 方法中,通过传递深度参数,将节点值添加到对应深度的结果列表中,实现了层序遍历的效果。
  4. 提前返回:在处理空树的情况时,提前返回空列表,避免了不必要的计算。

常见错误分析

  1. 忽略空树检查:忘记检查根节点是否为空,可能导致空指针异常。
  2. 层级划分错误:在 BFS 方法中,如果不正确记录当前层的节点数量,可能导致层级划分错误。
  3. 队列使用不当:在 BFS 方法中,如果忘记将节点的子节点入队,或者入队顺序错误,可能导致遍历不完整或顺序错误。
  4. 递归深度计算错误:在 DFS 方法中,如果深度参数计算错误,可能导致节点被添加到错误的层级中。

解法比较

方法时间复杂度空间复杂度优点缺点
BFSO(n)O(n)直观易懂,符合层序遍历的本质需要使用队列,空间复杂度较高
DFSO(n)O(h),h为树的高度递归实现简洁,空间复杂度可能更低不符合层序遍历的直观理解,可能不如 BFS 直观

相关题目