二叉树的最大宽度计算

【学习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函数没有在字符数组中找到逗号,返回npos
  • stoi()将字符串转为整数
  • erase()移除已处理的部分字符串

🔍 深入解析:处理输入字符串的最后一个元素

在解析输入字符串时,我们需要特别注意处理最后一个元素的情况。让我们仔细分析这段关键代码:

cpp 复制代码
// 处理最后一个元素
if (!arrStr.empty()) {
    if (arrStr == "null") {
        nodes.push_back(-1);
    } else {
        nodes.push_back(stoi(arrStr));
    }
}

为什么需要这段代码?

  1. 循环处理的局限性

    • 前面的while循环是通过查找逗号来分割字符串的
    • 当处理到最后一个元素时,字符串中可能不再有逗号
    • 例如输入 [1,2,3,null,5],处理完"3"和"null"后,剩下的"5"后面没有逗号
  2. 确保完整性

    • 这段代码确保不会遗漏输入数组的最后一个元素
    • 是整个解析过程的收尾工作

代码执行逻辑分析:

  1. if (!arrStr.empty())

    • 检查字符串是否还有剩余内容
    • 这是必要的防御性编程,避免对空字符串进行操作
  2. 处理"null"情况:

    cpp 复制代码
    if (arrStr == "null") {
        nodes.push_back(-1);
    }
    • 将"null"转换为-1(这是我们约定的空节点表示)
    • 例如输入中的最后一个元素是"null"
  3. 处理数字情况:

    cpp 复制代码
    else {
        nodes.push_back(stoi(arrStr));
    }
    • 使用stoi将字符串转换为整数
    • 例如输入中的最后一个元素是"5"

实际案例演示:

案例1 :输入 [1,2,3]

  • 处理流程:
    1. 提取"1",剩余"2,3"
    2. 提取"2",剩余"3"
    3. while循环结束,处理最后的"3"
  • 最终nodes内容:[1, 2, 3]

案例2 :输入 [1,null,3,null]

  • 处理流程:
    1. 提取"1",剩余"null,3,null"
    2. 提取"null"(转为-1),剩余"3,null"
    3. 提取"3",剩余"null"
    4. while循环结束,处理最后的"null"
  • 最终nodes内容:[1, -1, 3, -1]

为什么这样设计?

  1. 鲁棒性

    • 能够处理各种合法的输入格式
    • 包括以null结尾的数组
  2. 一致性

    • 确保所有元素(包括最后一个)都按照相同规则处理
    • 保持代码逻辑的统一性
  3. 边界条件处理

    • 专门处理输入数组只有一个元素的情况
    • 例如输入 [5] 也能正确解析

常见错误规避:

如果没有这段代码:

  • 最后一个元素会被遗漏
  • 导致构建的二叉树不完整
  • 可能引发数组越界或计算错误

扩展思考:

这种处理方式体现了几个重要的编程原则:

  1. 完整性:确保处理所有可能的输入情况
  2. 防御性编程:通过empty()检查避免运行时错误
  3. 一致性:对null和数字的处理与前面保持一致

💡 学习收获

  1. 递归思维:理解如何用递归遍历树结构并统计信息
  2. 字符串处理:掌握C++中字符串查找、分割和转换的方法
  3. 动态统计:学习如何动态扩展vector来记录层级信息
  4. 算法库应用 :使用max_element快速找到最大值

🚀 优化思考

当前实现使用了递归遍历,对于非常深的树可能会导致栈溢出。可以考虑改用**层序遍历(BFS)**来实现,这样既避免了递归深度问题,又能直观地统计每层节点数。

大家有没有其他计算二叉树宽度的方法呢?欢迎在评论区分享你的想法!如果觉得这篇内容有帮助,别忘了点赞收藏哦~

#编程 #算法学习 #二叉树 #C++ #LeetCode

相关推荐
大千AI助手2 小时前
DTW模版匹配:弹性对齐的时间序列相似度度量算法
人工智能·算法·机器学习·数据挖掘·模版匹配·dtw模版匹配
YuTaoShao3 小时前
【LeetCode 热题 100】48. 旋转图像——转置+水平翻转
java·算法·leetcode·职场和发展
生态遥感监测笔记3 小时前
GEE利用已有土地利用数据选取样本点并进行分类
人工智能·算法·机器学习·分类·数据挖掘
Tony沈哲4 小时前
macOS 上为 Compose Desktop 构建跨架构图像处理 dylib:OpenCV + libraw + libheif 实践指南
opencv·算法
刘海东刘海东4 小时前
结构型智能科技的关键可行性——信息型智能向结构型智能的转变(修改提纲)
人工智能·算法·机器学习
独行soc5 小时前
#渗透测试#批量漏洞挖掘#HSC Mailinspector 任意文件读取漏洞(CVE-2024-34470)
linux·科技·安全·网络安全·面试·渗透测试
pumpkin845145 小时前
Rust 调用 C 函数的 FFI
c语言·算法·rust
挺菜的5 小时前
【算法刷题记录(简单题)003】统计大写字母个数(java代码实现)
java·数据结构·算法
mit6.8245 小时前
7.6 优先队列| dijkstra | hash | rust
算法