【算法题】队列&广度优先搜索

层序遍历(广度优先搜索,BFS)是树结构最核心的遍历方式之一,核心逻辑是按"层"访问节点 ,通过队列实现"先进先出"的层级遍历特性。它不仅能解决基础的层级节点收集问题,还能扩展处理"锯齿形遍历""最大宽度计算""每层最大值查找"等进阶场景,是树类算法的高频考点。本文通过4道经典题目,拆解层序遍历在不同场景下的解题思路与代码实现(涵盖N叉树、二叉树的各类层级问题)。

一、N叉树的层序遍历

题目描述:

给定一个N叉树的根节点 root,返回其节点值的层序遍历(即按从左到右,逐层遍历节点值)。

示例

  • 输入:根节点为1,子节点为[3,2,4],3的子节点为[5,6]
  • 输出:[[1],[3,2,4],[5,6]]

解题思路:

标准层序遍历模板(队列+按层处理):

  1. 初始化队列,若根节点非空则入队。
  2. 循环处理队列:
    • 记录当前层的节点数 sz(队列大小);
    • 遍历当前层的 sz 个节点,收集节点值,同时将每个节点的所有子节点入队;
    • 将当前层的节点值存入结果数组。
  3. 遍历结束后返回结果。

完整代码:

cpp 复制代码
/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> ret;
        queue<Node*> q;
        if(!root) return ret;
        q.push(root);

        while(!q.empty())
        {
            int sz = q.size(); // 当前层的节点数
            vector<int> tmp;
            while(sz--)
            {
                auto t = q.front();
                q.pop();
                tmp.push_back(t->val);
                // 入队所有子节点
                for(auto& child : t->children)
                    q.push(child);
            }
            ret.push_back(tmp);
        }
        return ret;
    }
};

复杂度分析:

  • 时间复杂度:O(n)O(n)O(n),n 为N叉树的节点总数,每个节点仅入队/出队一次。
  • 空间复杂度:O(n)O(n)O(n),队列最多存储一层的节点(最坏情况为最后一层,节点数接近 nnn)。

二、二叉树的锯齿形层序遍历

题目描述:

给定二叉树的根节点 root,返回其节点值的锯齿形层序遍历(即先从左到右,再从右到左,交替遍历各层)。

示例

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

解题思路:

标准层序遍历 + 奇偶层反转:

  1. 基础逻辑与普通层序遍历一致,新增 level 变量记录当前层数(初始为1)。
  2. 若当前层数为偶数,将该层的节点值数组反转(实现从右到左);
  3. 层数递增,继续处理下一层。

完整代码:

cpp 复制代码
/**
 * 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:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ret;
        queue<TreeNode*> q;
        if(!root) return ret;
        q.push(root);
        int level = 1; // 记录当前层数

        while(!q.empty())
        {
            int sz = q.size();
            vector<int> tmp;
            for(int i = 0; i < sz; i++)
            {
                auto t = q.front();
                q.pop();
                tmp.push_back(t->val);
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            // 偶数层反转
            if(level % 2 == 0) reverse(tmp.begin(), tmp.end());
            ret.push_back(tmp);
            level++;
        }
        return ret;
    }
};

复杂度分析:

  • 时间复杂度:O(n)O(n)O(n),每个节点遍历一次,偶数层反转的总时间为 O(n)O(n)O(n)(所有层节点数之和为 nnn)。
  • 空间复杂度:O(n)O(n)O(n),队列存储层级节点,结果数组存储所有节点值(必要输出,不计入额外复杂度)。

三、二叉树的最大宽度

题目描述:

给定二叉树的根节点 root,返回其最大宽度(宽度定义为:某层最左和最右的非空节点之间的节点数,包括空节点但不包括超出范围的节点)。

示例

  • 输入:root = [1,3,2,5,3,null,9]
  • 输出:4(第三层宽度为4:5,3,null,9)

解题思路:

层序遍历 + 节点编号(模拟完全二叉树的编号规则):

  1. 用"节点+编号"的配对存储队列元素,根节点编号为1;
  2. 左子节点编号 = 父节点编号 × 2,右子节点编号 = 父节点编号 × 2 + 1;
  3. 每层计算最左节点和最右节点的编号差 + 1(即当前层宽度),更新最大宽度;
  4. unsigned int 避免编号溢出(二叉树深度较大时,编号可能超过int范围)。

完整代码:

cpp 复制代码
/**
 * 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:
    int widthOfBinaryTree(TreeNode* root) {
        vector<pair<TreeNode*, unsigned int>> q; // <节点, 编号>
        unsigned int ret = 0;
        if(!root) return 0;
        q.push_back({root, 1});

        while(!q.empty())
        {
            // 计算当前层宽度
            auto& [x1, y1] = q[0];
            auto& [x2, y2] = q.back();
            ret = max(ret, y2 - y1 + 1);

            // 入队下一层节点(仅非空节点)
            vector<pair<TreeNode*, unsigned int>> tmp;
            for(auto& [x, y] : q)
            {
                if(x->left) tmp.push_back({x->left, 2 * y});
                if(x->right) tmp.push_back({x->right, 2 * y + 1});
            }
            q = tmp;
        }

        return ret;
    }
};

复杂度分析:

  • 时间复杂度:O(n)O(n)O(n),每个节点仅入队/出队一次。
  • 空间复杂度:O(n)O(n)O(n),队列存储"节点+编号"的配对,最坏情况存储一层的所有节点。

四、在每个树行中找最大值

题目描述:

给定二叉树的根节点 root,返回每一层的最大值组成的数组。

示例

  • 输入:root = [1,3,2,5,3,null,9]
  • 输出:[1,3,9]

解题思路:

层序遍历 + 每层最大值维护:

  1. 基础逻辑与普通层序遍历一致;
  2. 遍历当前层的所有节点时,维护一个临时变量 tmp(初始为 INT_MIN),记录当前层的最大值;
  3. 每层遍历结束后,将最大值存入结果数组。

完整代码:

cpp 复制代码
/**
 * 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:
    vector<int> largestValues(TreeNode* root) {
        vector<int> ret;
        queue<TreeNode*> q;
        if(!root) return {};
        q.push(root);

        while(!q.empty())
        {
            int tmp = INT_MIN; // 初始化为最小值
            int sz = q.size();

            while(sz--)
            {
                auto t = q.front();
                q.pop();
                tmp = max(tmp, t->val); // 更新当前层最大值
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            ret.push_back(tmp);
        }
        return ret;
    }
};

复杂度分析:

  • 时间复杂度:O(n)O(n)O(n),每个节点仅遍历一次,每层最大值比较为常数级操作。
  • 空间复杂度:O(n)O(n)O(n),队列存储层级节点,结果数组存储每层最大值(必要输出,不计入额外复杂度)。
相关推荐
hetao17338373 小时前
2026-01-14~15 hetao1733837 的刷题笔记
c++·笔记·算法
好奇龙猫3 小时前
【大学院-筆記試験練習:线性代数和数据结构(10)】
数据结构·线性代数
百度搜不到…3 小时前
背包问题递推公式中的dp[j-nums[j]]到底怎么理解
算法·leetcode·动态规划·背包问题
一起养小猫3 小时前
LeetCode100天Day13-移除元素与多数元素
java·算法·leetcode
ACERT3333 小时前
10.吴恩达机器学习——无监督学习01聚类与异常检测算法
python·算法·机器学习
诗词在线3 小时前
从算法重构到场景复用:古诗词数字化的技术破局与落地实践
python·算法·重构
不穿格子的程序员3 小时前
从零开始写算法——二叉树篇7:从前序与中序遍历序列构造二叉树 + 二叉树的最近公共祖先
数据结构·算法
hetao17338373 小时前
2026-01-12~01-13 hetao1733837 的刷题笔记
c++·笔记·算法
曹仙逸3 小时前
数据结构day06小项目
数据结构
无限码力3 小时前
美团秋招笔试真题 - 放它一马 & 信号模拟
算法·美团秋招·美团笔试·美团笔试真题