# [222] 完全二叉树的节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^h 个节点。
示例 1:
输入:root = [1,2,3,4,5,6]
输出:6
示例 2:
输入:root = []
输出:0
示例 3:
输入:root = [1]
输出:1
提示:
树中节点的数目范围是[0, 5> 10^4]
题目数据保证输入的树是 完全二叉树
进阶:遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?
遍历树来统计节点十分基础,使用前中后序均可,这里简单写了一下前序遍历的解法:
function countNodes(root: TreeNode | null): number {
let res = 0;
traverse(root);
return res;
function traverse(node: TreeNode | null) {
if (!node) return;
res += 1;
traverse(node.left);
traverse(node.right);
}
}
这样解法的时间复杂度是O(n),那么,怎样优化达到更高的效率呢?
我们可以注意到,题目保证了给出的数是一个完全二叉树。完全二叉树有一个特性:一棵完全二叉树的两棵子树,至少有一棵是满二叉树。
那么满二叉树有什么用呢?满二叉树不需要遍历树来计算节点个数,可以直接使用数学推导,树高为N的满二叉树,节点共有 2^N - 1 个。
那么解法就呼之欲出了。通过递归完全二叉树中不为满二叉树的子树,不断得出另一侧满二叉树的子树节点,相加即可。
function countNodes(root: TreeNode | null): number {
return traverse(root);
function traverse(node: TreeNode | null): number {
if (!node) return 0;
// 记录左右子树的高度
let lHeight = 1,
rHeight = 1;
let lTree: TreeNode = node.left;
while (lTree) {
lTree = lTree.left;
lHeight += 1;
}
let rTree: TreeNode = node.right;
while (rTree) {
rTree = rTree.right;
rHeight += 1;
}
if (lHeight === rHeight) {
// 当前 node 为树高为 N 的满二叉树,节点共有 2^N - 1 个
return Math.pow(2, lHeight) - 1;
}
// 递归左右子树节点数,加上当前节点
return traverse(node.left) + traverse(node.right) + 1;
}
}