难度参考

        难度:中等

        分类:二叉树

        难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记,旨在督促自己认真学习。

题目

        给你二叉树的根节点root,返回其节点值的层序遍历。(即逐层地,从左到右访问所有节点)。
        示例1:
        输入:root=[3,9,20,null,null,15,7]
        输出:[3],[9,20],[15,7]
        提示:
        树中节点数目在范围[0,20]内
        -100<=Node.Val<=1000

思路

        对于这个问题,我们可以使用广度优先搜索(BFS)算法来进行二叉树的层序遍历。BFS 通常使用队列来实现,通过不断出队和入队的方式,将每一层的节点依次遍历。

  1. 首先,我们需要定义一个队列(queue)来存储待遍历的节点。初始时,将根节点(root)入队。
  2. 然后,进入循环,直到队列为空。在每一次循环中,分别执行以下步骤:
    • 从队列中取出当前节点,并将当前节点的值存入结果数组中(该层的结果数组)。
    • 如果当前节点的左子节点不为空,将左子节点入队。
    • 如果当前节点的右子节点不为空,将右子节点入队。
  3. 循环结束后,我们就能得到一个每层节点值的层序遍历结果数组。

        最后,根据题目的要求,返回层序遍历的结果数组。

示例

        假设我们有以下的二叉树:

     3
    / \
   9  20
     /  \
    15   7

        我们可以按照层序遍历的顺序逐层访问节点。根据题目要求,层序遍历的结果数组应该是 [3], [9, 20], [15, 7]

  1. 首先,将根节点 3 入队。
         3                     --------------------
        / \                      3
       9  20                   --------------------        
         /  \
        15   7                 result = {}
    
  2. 开始循环,第一层级只有根节点 3,所以当前队列中只有根节点 3。将根节点 3 出队,并将其值 3 放入当前层级结果数组中。然后,检查节点 3 的左右子节点,发现都不为空,将根节点的左子节点 9 入队,右子节点 20 入队。
         3                     --------------------
        / \                      9, 20
       9  20                   --------------------        
         /  \
        15   7                 result = {3}
    
  3. 进入下一层级循环。此时,队列中有两个节点,分别是节点 9 和节点 20。将节点 9 出队,并将其值 9 放入当前层级结果数组中。然后,检查节点 9 的左右子节点,发现都为空,所以不需要入队。接着,将节点 20 出队,并将其值 20 放入当前层级结果数组中。然后,将节点 20 的左子节点 15 入队,右子节点 7 入队。
         3                     --------------------
        / \                      15, 7
       9  20                   --------------------        
         /  \
        15   7                 result = {3, 9, 20}
    
  4. 再次进入下一层级循环。此时,队列中有两个节点,分别是节点 15 和节点 7。将节点 15 出队,并将其值 15 放入当前层级结果数组中。然后,检查节点 15 的左右子节点,发现都为空,所以不需要入队。接着,将节点 7 出队,并将其值 7 放入当前层级结果数组中。然后,检查节点 7 的左右子节点,发现都为空,所以不需要入队。
         3                     --------------------
        / \                      
       9  20                   --------------------        
         /  \
        15   7                 result = {3, 9, 20, 15, 7}
    
  5. 循环结束,得到层序遍历的结果数组 [3], [9, 20], [15, 7]

梳理

        对于二叉树的层序遍历,使用队列是因为队列的先进先出(FIFO)特性能够确保每一层的节点按照从左到右的顺序被访问

        具体来说,层序遍历的思路是从根节点开始,依次将节点的左子节点和右子节点加入到队列中,然后从队列中取出节点进行访问。这样做可以保证每一层的节点按照从左到右的顺序进行访问,因为队列中先入队的节点会先出队。

        通过使用队列,保证了层序遍历的节点访问顺序正确,能够按照层级逐步扩展,并且能够将每一层的节点按照顺序存储到结果数组中。

        具体流程中,我们通过循环不断从队列中取出节点,并将其值存入结果数组中。然后,检查当前节点的左右子节点,如果存在则将其加入到队列中,以便后续的访问。

        总结来说,队列在层序遍历中起到了承载节点的作用,能够确保每一层的节点按照顺序被访问。通过队列的先进先出特性,我们能够按照层级逐步扩展,从而得到二叉树的层序遍历结果。

算法练习-二叉树的层序遍历(思路+流程图+代码)-LMLPHP

代码

#include <iostream> // 包含输入输出流库
#include <queue> // 包含队列库
#include <vector> // 包含向量库

using namespace std; // 使用标准命名空间

// 定义二叉树的节点
struct TreeNode {
    int val; // 节点值
    TreeNode* left; // 左子节点指针
    TreeNode* right; // 右子节点指针
    
    // 构造函数
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 层序遍历函数
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 size = q.size();
        vector<int> levelNodes; // 存放当前层节点值的向量
        
        // 遍历当前层的节点
        for (int i = 0; i < size; i++) {
            // 弹出队列首节点,并将其值存入结果数组中
            TreeNode* node = q.front();
            q.pop();
            levelNodes.push_back(node->val);
            
            // 将当前节点的左子节点和右子节点入队
            if (node->left) {
                q.push(node->left);
            }
            
            if (node->right) {
                q.push(node->right);
            }
        }
        
        // 将当前层的节点值数组存入结果数组中
        result.push_back(levelNodes);
    }
    
    // 返回结果数组
    return result;
}

int main() {
    // 创建示例树 [3, 9, 20, null, null, 15, 7]
    TreeNode* root = new TreeNode(3);
    root->left = new TreeNode(9);
    root->right = new TreeNode(20);
    root->right->left = new TreeNode(15);
    root->right->right = new TreeNode(7);

    // 执行层序遍历
    vector<vector<int>> result = levelOrder(root);

    // 输出结果
    for (const auto& level : result) { // 遍历每一层
        cout << "["; // 给输出结果添加左括号
        for (const auto& val : level) { // 遍历当前层的节点值
            cout << val << ","; // 输出节点值和逗号
        }
        cout << "]"; // 给输出结果添加右括号
    }
    
    return 0;
}

        时间复杂度:O(n)
        空间复杂度:O(n)

  1. pop 是队列的操作,用于移除队列中的队头元素(第一个元素)。
  2. push 是队列的操作,用于将元素添加到队尾。
  3. push_back 是向量的操作,用于将元素添加到向量的末尾。

        在代码中,首先创建一个空的队列 q,然后使用 q.push(root) 将根节点 root 添加到队列中。接下来,通过循环遍历队列,每次从队列中取出一个节点,并将其值添加到 levelNodes 向量中。
同时,如果节点有左子节点或右子节点,通过 q.push 将它们添加到队列的尾部,以便在后续循环中处理它们。
        最后,将 levelNodes 添加到 result 向量中,表示当前层的节点值。
整个过程将重复直到队列为空。最后返回 result 向量,即为二叉树的层序遍历结果。

        在主函数中,示例了如何使用 levelOrder 函数来执行对创建的二叉树进行层序遍历,并将结果输出到屏幕上。

打卡

算法练习-二叉树的层序遍历(思路+流程图+代码)-LMLPHP

02-02 08:17