层序遍历(广度优先搜索,BFS)是树结构最核心的遍历方式之一,核心逻辑是按"层"访问节点 ,通过队列实现"先进先出"的层级遍历特性。它不仅能解决基础的层级节点收集问题,还能扩展处理"锯齿形遍历""最大宽度计算""每层最大值查找"等进阶场景,是树类算法的高频考点。本文通过4道经典题目,拆解层序遍历在不同场景下的解题思路与代码实现(涵盖N叉树、二叉树的各类层级问题)。
一、N叉树的层序遍历
题目描述:
给定一个N叉树的根节点 root,返回其节点值的层序遍历(即按从左到右,逐层遍历节点值)。
示例:
- 输入:根节点为1,子节点为[3,2,4],3的子节点为[5,6]
- 输出:
[[1],[3,2,4],[5,6]]
解题思路:
标准层序遍历模板(队列+按层处理):
- 初始化队列,若根节点非空则入队。
- 循环处理队列:
- 记录当前层的节点数
sz(队列大小); - 遍历当前层的
sz个节点,收集节点值,同时将每个节点的所有子节点入队; - 将当前层的节点值存入结果数组。
- 记录当前层的节点数
- 遍历结束后返回结果。
完整代码:
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]]
解题思路:
标准层序遍历 + 奇偶层反转:
- 基础逻辑与普通层序遍历一致,新增
level变量记录当前层数(初始为1)。 - 若当前层数为偶数,将该层的节点值数组反转(实现从右到左);
- 层数递增,继续处理下一层。
完整代码:
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;
- 左子节点编号 = 父节点编号 × 2,右子节点编号 = 父节点编号 × 2 + 1;
- 每层计算最左节点和最右节点的编号差 + 1(即当前层宽度),更新最大宽度;
- 用
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]
解题思路:
层序遍历 + 每层最大值维护:
- 基础逻辑与普通层序遍历一致;
- 遍历当前层的所有节点时,维护一个临时变量
tmp(初始为INT_MIN),记录当前层的最大值; - 每层遍历结束后,将最大值存入结果数组。
完整代码:
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),队列存储层级节点,结果数组存储每层最大值(必要输出,不计入额外复杂度)。