【优选算法篇】队列与宽度优先搜索(BFS)——层层递进的视野

文章目录

    • [BFS 的奥义:涟漪式扩散,掌控全局](#BFS 的奥义:涟漪式扩散,掌控全局)
    • [一、 N 叉树的层序遍历:打破维度的墙 (Medium)](#一、 N 叉树的层序遍历:打破维度的墙 (Medium))
      • [1.1 题目描述](#1.1 题目描述)
      • [1.2 算法思路:队列控制层级](#1.2 算法思路:队列控制层级)
      • [1.3 C++ 代码实战](#1.3 C++ 代码实战)
    • [二、 二叉树的锯齿形层序遍历:灵活的翻转 (Medium)](#二、 二叉树的锯齿形层序遍历:灵活的翻转 (Medium))
      • [2.1 题目描述](#2.1 题目描述)
      • [2.2 算法思路:偶数层大反转](#2.2 算法思路:偶数层大反转)
      • [2.3 C++ 代码实战](#2.3 C++ 代码实战)
    • [三、 二叉树的最大宽度:坐标映射的威力 (Medium)](#三、 二叉树的最大宽度:坐标映射的威力 (Medium))
      • [3.1 题目描述](#3.1 题目描述)
      • [3.2 深度拆解:索引编号法](#3.2 深度拆解:索引编号法)
      • [3.3 C++ 代码实战](#3.3 C++ 代码实战)
    • [四、 在每个树行中找最大值 (Medium)](#四、 在每个树行中找最大值 (Medium))
      • [4.1 题目描述](#4.1 题目描述)
      • [4.2 算法思路](#4.2 算法思路)
      • [4.3 C++ 代码实战](#4.3 C++ 代码实战)
    • [五、 总结:BFS 的涟漪扩散本质](#五、 总结:BFS 的涟漪扩散本质)

BFS 的奥义:涟漪式扩散,掌控全局


一、 N 叉树的层序遍历:打破维度的墙 (Medium)

1.1 题目描述

题目链接429. N 叉树的层序遍历

描述

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

1.2 算法思路:队列控制层级

BFS 的标准模板:

  1. 入队起点:先把根节点扔进队列。
  2. 按层处理 :记录当前队列的大小 sz,这代表了这一层有多少个节点
  3. 循环扩展 :处理完这 sz 个节点,并把它们的所有孩子依次入队。

核心技巧 :通过 q.size() 确定当前层的数量,可以完美区分出结果数组里的每一行。

1.3 C++ 代码实战

cpp 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> ret;
        if (!root) return ret;

        queue<Node*> q;
        q.push(root);

        while (!q.empty()) {
            int sz = q.size(); // 锁定当前层的节点数
            vector<int> currentLevel;

            for (int i = 0; i < sz; i++) {
                Node* t = q.front();
                q.pop();
                currentLevel.push_back(t->val);

                // N 叉树:遍历所有孩子并入队
                for (Node* child : t->children) {
                    if (child) q.push(child);
                }
            }
            ret.push_back(currentLevel);
        }
        return ret;
    }
};

二、 二叉树的锯齿形层序遍历:灵活的翻转 (Medium)

2.1 题目描述

题目链接103. 二叉树的锯齿形层序遍历

描述

先从左往右,再从右往左,层与层之间交替进行。

2.2 算法思路:偶数层大反转

这题没必要在入队、出队顺序上纠结得半死。

  • 第一步 :按照正常的层序遍历,把每一层的数据存进 vector<int> tmp
  • 第二步 :维护一个层数变量 level(从 1 开始)。如果是偶数层,直接把 tmp 反转(reverse) 即可。

ASCII 逻辑图

bash 复制代码
L1: [3] -> 正常
L2: [9, 20] -> 偶数层, 反转 -> [20, 9]
L3: [15, 7] -> 奇数层, 正常

2.3 C++ 代码实战

cpp 复制代码
class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ret;
        if (!root) return ret;

        queue<TreeNode*> q;
        q.push(root);
        int level = 1;

        while (!q.empty()) {
            int sz = q.size();
            vector<int> tmp;
            for (int i = 0; i < sz; i++) {
                TreeNode* 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;
    }
};

三、 二叉树的最大宽度:坐标映射的威力 (Medium)

3.1 题目描述

题目链接662. 二叉树最大宽度

描述

每一层的宽度定义为该层最左和最右非空节点之间的长度(包括中间的 null 节点)。

3.2 深度拆解:索引编号法

这道题不能直接数节点,因为 null 也占位置,如果直接计算时间和空间复杂度都会爆炸。

  • 破局点:利用完全二叉树的性质给节点编号。

    • 根节点编号为 i
    • 左孩子编号为 2 * i
    • 右孩子编号为 2 * i + 1
  • 计算宽度 :每一层的宽度 = 最右节点编号 - 最左节点编号 + 1

细节坑点 :二叉树极深时,编号会溢出 ,远超long long
解药 :使用 unsigned int。在 C++ 中,无符号整型溢出后相减,其结果依然是正确的"距离"(环形模运算特性)。(前提是这两个溢出的数值之差不能超过unsigned int表示最大值,也就是不能超过这个环的一圈)

3.3 C++ 代码实战

cpp 复制代码
class Solution {
public:
    int widthOfBinaryTree(TreeNode* root) {
        if (!root) return 0;
        
        // 队列存节点及其对应的编号 (unsigned int 防止溢出报错)
        queue<pair<TreeNode*, unsigned int>> q;
        q.push({root, 1});
        unsigned int maxWidth = 0;

        while (!q.empty()) {
            int sz = q.size();
            unsigned int leftIdx = q.front().second; // 拿到本层最左编号
            unsigned int rightIdx = 0;

            for (int i = 0; i < sz; i++) {
                auto [node, idx] = q.front();
                q.pop();
                rightIdx = idx; // 循环结束时,rightIdx 就是最右编号

                if (node->left) q.push({node->left, idx * 2});
                if (node->right) q.push({node->right, idx * 2 + 1});
            }
            maxWidth = max(maxWidth, rightIdx - leftIdx + 1);
        }
        return (int)maxWidth;
    }
};

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

4.1 题目描述

题目链接515. 在每个树行中找最大值

4.2 算法思路

这是 BFS 最简单的变体。在处理每一层的 sz 个节点时,顺便维护一个 maxVal 变量,遍历完一层后存入结果数组。

4.3 C++ 代码实战

cpp 复制代码
class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int> ret;
        if (!root) return ret;

        queue<TreeNode*> q;
        q.push(root);

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

            for (int i = 0; i < sz; i++) {
                TreeNode* t = q.front();
                q.pop();
                maxVal = max(maxVal, t->val);

                if (t->left) q.push(t->left);
                if (t->right) q.push(t->right);
            }
            ret.push_back(maxVal);
        }
        return ret;
    }
};

五、 总结:BFS 的涟漪扩散本质

💬 复盘

  1. 层级控制的核心

    BFS 的灵魂在于"按层推进 "。通过 q.size() 锁定当前层节点数,我们就能精准地将问题拆分成一层一层的子问题。

  2. 扩散模型

    BFS 本质是一种从起点向外一圈一圈扩散的过程,就像水波(涟漪)一样:

    • 当前层 = 当前波纹
    • 入队孩子 = 下一层波纹的扩展
  3. 状态附加技巧

    当题目需要额外信息时(如编号、方向、距离等),可以在队列中存:

    cpp 复制代码
    pair<节点, 状态>

    👉 例如:

    • 最大宽度:存 (node, index)
    • 最短路径:存 (node, distance)
  4. 结构不变,逻辑可变

    BFS 的框架几乎固定:

    cpp 复制代码
    while (!q.empty()) {
        int sz = q.size();
        // 处理当前层
    }

    👉 真正变化的是:

    • 每层"统计什么"(最大值 / 顺序 / 宽度)
    • 是否对结果做"变换"(如 reverse)
  5. 降维思想

    BFS 的强大之处在于:

    👉 把"复杂结构问题"转化为"逐层处理问题"

    每一层都是一个"线性问题",难度被大幅降低。


🚀 一句话总结

text 复制代码
BFS = 队列 + 分层 + 扩散

🔥 核心直觉

👉 只要题目出现:

  • "逐层"
  • "最短路径"
  • "扩散 / 传播"

第一反应:BFS!

相关推荐
floret. 小花2 小时前
Vue3 知识点总结 · 2026-03-27
前端·面试·electron·学习笔记·vue3
hanlin032 小时前
动态规划专练:力扣第509、70、746题
算法·leetcode·动态规划
CoderIsArt2 小时前
shor算法
算法·量子计算
m0_743890512 小时前
3月27日 模拟题
算法
REDcker2 小时前
ARMv8、AArch64 与 arm64:命名与体系结构要点
开发语言·c++·arm
北顾笙9803 小时前
day09-数据结构力扣
数据结构·算法·leetcode
旖-旎3 小时前
位运算(判断字符是否唯一)(1)
c++·算法·leetcode·位运算
词元Max3 小时前
1.4 核心名词解释:Token、RAG、Agent、MCP是什么
人工智能·算法
ouliten3 小时前
C++笔记:标签分派
c++·笔记