递归统计二叉树节点数只需 return 1 + count(root->left) + count(root->right),因每个节点贡献1,空节点返回0为终止条件;迭代法可用栈(dfs)或队列(bfs),结果相同但空间复杂度分别为o(h)和o(w),须注意指针有效性与容器类型匹配。

递归统计二叉树节点数:为什么直接 return 1 + count(root->left) + count(root->right) 就够了
递归写法本质是“每个节点贡献 1 个计数,再把左右子树的计数加进来”。只要树结构合法(root 为 nullptr 时返回 0),就不会漏掉或重复。关键在于终止条件必须明确:
-
if (!root) return 0;—— 空节点不计数,这是递归基,缺了就会崩溃 - 不要在递归前做额外判断(比如先检查
root->left是否为空再调用),反而破坏简洁性 - 如果节点定义里有
count字段缓存值,递归法会忽略它——它只看结构,不依赖存储
迭代法用栈还是队列?BFS 和 DFS 统计结果一样但行为不同
迭代法本质是手动模拟系统栈或层序遍历过程,两种方式结果一致,但内存使用和访问顺序不同:
- DFS(用
stack):压入根,每次 pop 一个节点,计数 +1,再 push 非空子节点;最坏空间复杂度 O(h),h 是树高 - BFS(用
queue):从根开始逐层扩展,每次取 front、计数 +1、push 非空子节点;最坏空间复杂度 O(w),w 是最大层宽 - 别混用容器类型——比如用
queue却按 DFS 顺序 push(右子树先于左子树),逻辑就乱了
示例(DFS 迭代):
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
int countNodes(TreeNode* root) {
if (!root) return 0;
stack<treenode> s;
s.push(root);
int cnt = 0;
while (!s.empty()) {
TreeNode* n = s.top(); s.pop();
cnt++;
if (n->right) s.push(n->right); // 注意:先右后左,才能让左子树先处理(可选,不影响总数)
if (n->left) s.push(n->left);
}
return cnt;
}</treenode>
遇到 nullptr 成员访问崩溃?检查指针有效性比猜逻辑更可靠
90% 的运行时崩溃来自对 nullptr 的 ->left 或 ->right 访问。不是所有编译器都开 -fsanitize=address,所以得自己守好边界:
- 递归中每进入函数第一行就判
if (!root) return 0;,别往后拖 - 迭代中每次从容器取节点后,先用
if (n)再访问成员,哪怕你“觉得不可能为空” - 如果用智能指针(如
shared_ptr<treenode></treenode>),比较要写成if (ptr),不是if (ptr != nullptr)(虽然后者也对,但前者更惯用)
统计全树节点数时,别被“完全二叉树优化”带偏方向
看到“二叉树节点统计”,有人立刻想到利用完全二叉树性质(通过最左/最右深度判断是否满,再用 2^h - 1),但这属于特例优化:
- 题目没说树形,一律按普通二叉树处理;强行套用会多出 log h 次深度计算,且代码复杂度飙升
- 只有当你明确知道输入一定是完全二叉树,且性能瓶颈真出现在这里(比如百万级节点+高频调用),才值得引入
- 日常开发或面试实现,老老实实递归或迭代更安全、易验、易维护
真正容易被忽略的是:迭代法里容器元素类型必须匹配节点指针类型,写成 stack<treenode></treenode>(值语义)会导致拷贝构造失败或未定义行为,必须是 stack<treenode></treenode> 或 stack<shared_ptr>></shared_ptr>。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










