必须用一次后序遍历返回结构体Result{height, leaf_count, paths},避免三次遍历;空节点返回{0,0,{}},叶子节点返回{1,1,{{val}}},非叶子节点合并左右路径并move优化,高度取max(left.height,right.height)+1,叶子数直接相加。

怎么一次性遍历就拿到叶子数、所有路径和树高
不能靠三次独立遍历——那样时间复杂度是 O(3n),还重复递归栈开销。必须用一次后序遍历同时收集三类信息:leaf_count、all_paths(vectorheight。关键在于每个节点返回结构体,而不是只返回高度。
常见错误是把 all_paths 当作全局 vector 传引用进去,导致路径错乱或重复 push;或者在非叶子节点也往 all_paths 里塞中间结果。
- 定义返回结构体:
struct Result { int height; int leaf_count; vector<vector>> paths; };</vector> - 叶子节点:
height = 1,leaf_count = 1,paths = {{val}} - 非叶子节点:合并左右子树的
paths,每个路径前头加当前val;height = max(left.height, right.height) + 1;leaf_count = left.leaf_count + right.leaf_count - 空节点返回:
{0, 0, {}}(注意 height=0,不是 -1)
为什么用后序遍历而不是前序或层序
前序遍历无法在访问根时知道子树高度和叶子分布;层序遍历能统计叶子数、算高度,但重建完整路径代价高(需额外存 parent 指针或路径数组)。后序天然满足“先得子结果,再合成父结果”的需求。
典型坑:有人试图用前序 + 临时 path vector 回溯来收集路径,这确实可行,但无法同时高效得高度和叶子数——因为高度必须从底往上推,而前序是从顶往下走。
立即学习“C++免费学习笔记(深入)”;
- 后序递归栈深度 = 树高,空间复杂度 O(h),和树形态强相关
- 如果树极度不平衡(链状),递归可能爆栈;生产环境建议改用显式栈的迭代后序(用 pair
标记是否已处理子树) - 路径存储开销大:最坏情况(满二叉树)有 2^(h-1) 条路径,每条长 h,总空间 O(h × 2^h)
std::vector 嵌套拷贝太慢?怎么避免路径复制爆炸
每次合并左右子树路径时,若直接 for (auto& p : left.paths) { p.insert(p.begin(), root->val); all_paths.push_back(p); },会产生大量 vector 拷贝。尤其在深树中,性能断崖下跌。
正确做法是 move 语义 + reserve 预分配:
- 左子树路径全部 move 进结果:
for (auto&& p : left.paths) { p.insert(p.begin(), root->val); all_paths.push_back(std::move(p)); } - 右子树同理,且提前
all_paths.reserve(left.paths.size() + right.paths.size()) - 更进一步:路径不用存 int,改存指针或索引(如
vector<const treenode></const>),避免整数拷贝;最终输出时再解引用取值 - 如果只关心路径数量或某条最长路径,根本不必存全部路径——用一个
max_pathvector 动态更新即可
空树、单节点、只有左/右子树这些边界怎么不出错
空树时,leaf_count = 0,height = 0,paths 为空 vector —— 这是标准定义,别写成 -1 或 1。单节点树是叶子,height = 1,leaf_count = 1,paths = {{val}}。
容易漏掉的 case:只有左子树(右为空)或只有右子树(左为空)。此时不能忽略空子树的贡献——它的 leaf_count 是 0,height 是 0,但参与 max 高度计算时必须考虑(否则高度会少算)。
- 高度公式必须是:
height = max(left.height, right.height) + 1,哪怕一边是 0 - 叶子数直接相加:
leaf_count = left.leaf_count + right.leaf_count,空子树贡献 0,天然安全 - 路径合并时,空子树的
paths是空 vector,循环不执行,无副作用 - 测试务必覆盖:
nullptr、单节点、左斜树、右斜树、满二叉树(3 层、4 层)
路径和高度可以一起算,但叶子统计看似简单,最容易在空树或单边树时逻辑分支写错——盯住“叶子”的定义:没有左右孩子的节点,跟是否为空子树无关。


















