【学习Vlog】深入理解二叉树的最大宽度计算:从输入解析到递归遍历
大家好!今天我要分享一个关于二叉树最大宽度计算的编程实践。通过这个例子,我们可以学到如何从字符串输入构建二叉树,以及如何用递归方式统计每层节点数。下面是我的学习笔记和代码分析~
🌳 二叉树最大宽度问题
最大宽度指的是二叉树中节点最多的那一层的节点数量。例如:
markdown
1
/ \
2 3
/ \ \
4 5 7
这棵树的最大宽度是3(第二层有3个节点)
📝 完整代码实现
cpp
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 构建二叉树的函数
TreeNode* buildTree(const vector<int>& nodes, int index) {
if (index >= nodes.size() || nodes[index] == -1) { // -1表示null
return nullptr;
}
TreeNode* root = new TreeNode(nodes[index]);
root->left = buildTree(nodes, 2 * index + 1);
root->right = buildTree(nodes, 2 * index + 2);
return root;
}
// 统计每层节点数的递归函数
void getWidth(TreeNode* root, int level, vector<int>& count) {
if (root == nullptr) return;
if (level >= count.size()) {
count.push_back(0);
}
count[level]++;
getWidth(root->left, level + 1, count);
getWidth(root->right, level + 1, count);
}
// 计算最大宽度的主函数
int widthOfBinaryTree(TreeNode* root) {
if (root == nullptr) return 0;
vector<int> count;
getWidth(root, 0, count);
return *max_element(count.begin(), count.end());
}
int main() {
string input;
getline(cin, input);
// 解析输入数组
vector<int> nodes;
size_t start = input.find('[');
size_t end = input.find(']');
string arrStr = input.substr(start + 1, end - start - 1);
size_t pos = 0;
while ((pos = arrStr.find(',')) != string::npos) {
string token = arrStr.substr(0, pos);
if (token == "null") {
nodes.push_back(-1);
} else {
nodes.push_back(stoi(token));
}
arrStr.erase(0, pos + 1);
}
// 处理最后一个元素
if (!arrStr.empty()) {
if (arrStr == "null") {
nodes.push_back(-1);
} else {
nodes.push_back(stoi(arrStr));
}
}
TreeNode* root = buildTree(nodes, 0);
cout << widthOfBinaryTree(root) << endl;
return 0;
}
🔍 关键知识点解析
1. 递归统计层节点数
cpp
void getWidth(TreeNode* root, int level, vector<int>& count) {
if (root == nullptr) return;
// 如果当前层级的计数还未初始化
if (level >= count.size()) {
count.push_back(0);
}
count[level]++; // 当前层级节点数+1
// 递归处理左右子树
getWidth(root->left, level + 1, count);
getWidth(root->right, level + 1, count);
}
level
参数记录当前节点所在的层级(根节点为0层)count
向量动态记录各层节点数- 采用前序遍历的方式访问每个节点
✨利用count动态数组联系起各层关系
ps.虽然在树的概念中root所在的是第一层,但是此处为了符合count数组的索引特性,采用从0开始
2. 输入字符串解析技巧
cpp
size_t start = input.find('[');
size_t end = input.find(']');
string arrStr = input.substr(start + 1, end - start - 1);
- 使用
find()
定位方括号位置,并返回索引 substr()
提取方括号内的内容size_t
是无符号整数类型,适合表示字符串索引
3. 逗号分隔值处理
cpp
while ((pos = arrStr.find(',')) != string::npos) {
string token = arrStr.substr(0, pos);
// 处理token...
arrStr.erase(0, pos + 1); // 删除已处理的部分
}
string::npos
表示未找到的特殊值,即如果find函数没有在字符数组中找到逗号,返回nposstoi()
将字符串转为整数erase()
移除已处理的部分字符串
🔍 深入解析:处理输入字符串的最后一个元素
在解析输入字符串时,我们需要特别注意处理最后一个元素的情况。让我们仔细分析这段关键代码:
cpp
// 处理最后一个元素
if (!arrStr.empty()) {
if (arrStr == "null") {
nodes.push_back(-1);
} else {
nodes.push_back(stoi(arrStr));
}
}
为什么需要这段代码?
-
循环处理的局限性:
- 前面的while循环是通过查找逗号来分割字符串的
- 当处理到最后一个元素时,字符串中可能不再有逗号
- 例如输入
[1,2,3,null,5]
,处理完"3"和"null"后,剩下的"5"后面没有逗号
-
确保完整性:
- 这段代码确保不会遗漏输入数组的最后一个元素
- 是整个解析过程的收尾工作
代码执行逻辑分析:
-
if (!arrStr.empty())
:- 检查字符串是否还有剩余内容
- 这是必要的防御性编程,避免对空字符串进行操作
-
处理"null"情况:
cppif (arrStr == "null") { nodes.push_back(-1); }
- 将"null"转换为-1(这是我们约定的空节点表示)
- 例如输入中的最后一个元素是"null"
-
处理数字情况:
cppelse { nodes.push_back(stoi(arrStr)); }
- 使用
stoi
将字符串转换为整数 - 例如输入中的最后一个元素是"5"
- 使用
实际案例演示:
案例1 :输入 [1,2,3]
- 处理流程:
- 提取"1",剩余"2,3"
- 提取"2",剩余"3"
- while循环结束,处理最后的"3"
- 最终nodes内容:[1, 2, 3]
案例2 :输入 [1,null,3,null]
- 处理流程:
- 提取"1",剩余"null,3,null"
- 提取"null"(转为-1),剩余"3,null"
- 提取"3",剩余"null"
- while循环结束,处理最后的"null"
- 最终nodes内容:[1, -1, 3, -1]
为什么这样设计?
-
鲁棒性:
- 能够处理各种合法的输入格式
- 包括以null结尾的数组
-
一致性:
- 确保所有元素(包括最后一个)都按照相同规则处理
- 保持代码逻辑的统一性
-
边界条件处理:
- 专门处理输入数组只有一个元素的情况
- 例如输入
[5]
也能正确解析
常见错误规避:
如果没有这段代码:
- 最后一个元素会被遗漏
- 导致构建的二叉树不完整
- 可能引发数组越界或计算错误
扩展思考:
这种处理方式体现了几个重要的编程原则:
- 完整性:确保处理所有可能的输入情况
- 防御性编程:通过empty()检查避免运行时错误
- 一致性:对null和数字的处理与前面保持一致
💡 学习收获
- 递归思维:理解如何用递归遍历树结构并统计信息
- 字符串处理:掌握C++中字符串查找、分割和转换的方法
- 动态统计:学习如何动态扩展vector来记录层级信息
- 算法库应用 :使用
max_element
快速找到最大值
🚀 优化思考
当前实现使用了递归遍历,对于非常深的树可能会导致栈溢出。可以考虑改用**层序遍历(BFS)**来实现,这样既避免了递归深度问题,又能直观地统计每层节点数。
大家有没有其他计算二叉树宽度的方法呢?欢迎在评论区分享你的想法!如果觉得这篇内容有帮助,别忘了点赞收藏哦~
#编程 #算法学习 #二叉树 #C++ #LeetCode