直接 return 1 + left + right 就够了,因为每个节点只需统计自身(1个)、左子树节点数和右子树节点数,递归终止于空节点返回0,无需额外变量或遍历逻辑。

递归统计总节点数:为什么直接 return 1 + left + right 就够了
递归是最直观的解法,核心在于每个节点只负责统计自己 + 左子树节点数 + 右子树节点数。关键不是“遍历”,而是“分解”——把大问题拆成三个小问题:当前节点(1个)、左子树、右子树。
常见错误是漏掉空节点判断,导致空指针解引用:nullptr 不能调用 left 或 right 成员。必须先判空:
int countNodes(TreeNode* root) {
if (!root) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}-
root为nullptr时立刻返回 0,这是递归终止条件,不可省略 - 不要在递归前加额外逻辑(比如计数器变量),会破坏纯函数性,也容易引发作用域错误
- 时间复杂度 O(n),空间复杂度 O(h),h 是树高;最坏情况(链状树)栈深度达 n,可能爆栈
迭代统计总节点数:用 queue 还是 stack?选 queue 更自然
迭代本质是手动模拟递归调用栈,但 BFS(层序)比 DFS(前序/中序/后序)更贴近“计数”语义——每访问一个节点就累加一次,逻辑直白,不易错。
用 stack 做 DFS 迭代也能做,但需要额外维护访问状态(比如是否已处理子节点),反而增加出错概率。BFS 用 queue 只需无脑 push/pop:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
int countNodes(TreeNode* root) {
if (!root) return 0;
int count = 0;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front(); q.pop();
count++;
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
return count;
}- 必须初始化
count = 0,且在 pop 后立即count++,不能放到 push 里——否则空节点也会被计数 -
queue头文件是<queue>,别漏 include;TreeNode*类型要和你的定义一致(比如有的题用struct TreeNode) - 空间复杂度 O(w),w 是最大宽度;满二叉树最坏是 O(n/2),比递归栈更可控
遇到完全二叉树?别硬套通用解法,用位运算+高度优化到 O(log²n)
通用递归/迭代都是 O(n),但如果题目明确说“完全二叉树”,就有捷径:利用其结构规律——除最后一层外全满,且最后一层靠左。此时可结合树高和最左/最右路径判断子树是否满。
核心技巧是:先算左子树高度 leftHeight 和右子树高度 rightHeight。若相等,说明左子树是满二叉树,节点数为 2^leftHeight - 1;否则递归查右子树。
int countNodes(TreeNode* root) {
if (!root) return 0;
int left = getHeight(root->left), right = getHeight(root->right);
if (left == right) {
return (1 << left) + countNodes(root->right); // 左子树满:2^left 个节点(含根)
} else {
return (1 << right) + countNodes(root->left);
}
}
int getHeight(TreeNode* node) {
int h = 0;
while (node) { node = node->left; h++; }
return h;
}1 等价于 <code>pow(2, left),但更快更安全;注意这里是2^left,不是2^left - 1,因为满子树节点数含当前根-
getHeight只走最左路径,O(log n);每次递归至少砍掉一层,总复杂度 O(log²n) - 这个优化只对完全二叉树有效;普通二叉树强行套用反而更慢,还容易写错高度计算逻辑
LeetCode 提交失败?检查 TreeNode 定义和 nullptr 处理是否匹配
本地跑通不代表能 AC。常见坑是:题目给的 TreeNode 结构体字段名不一致(比如有的用 val/left/right,有的用 data/lchild),或者测试用例含大量 nullptr 子节点。
- 务必复制题目给出的
TreeNode定义,不要自己重写;尤其注意构造函数是否存在、是否默认初始化指针 - 所有指针访问前必须判空,包括
root->left和root->right—— 即使你认为“不会为空”,测试数据可能故意构造单边树 - 递归版本在极端深树下可能栈溢出(如 n=10⁵ 的链状树),此时必须切迭代;LeetCode 默认栈大小有限,不保证递归安全
真正难的不是写对一行 return,而是想清楚:当前树是不是完全二叉树、输入规模会不会压垮递归、指针成员名有没有和题干对齐。这些细节卡住的人,远多于不知道怎么写递归的人。

















