Easy
题目描述
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~2^h 个节点。
请设计一个时间复杂度小于 O(n) 的算法来求解。
示例 1:
输入:root = [1,2,3,4,5,6]
输出:6
示例 2:
输入:root = []
输出:0
示例 3:
输入:root = [1]
输出:1
提示:
- 树中节点的数目范围是
[0, 5 * 10^4] 0 <= Node.val <= 5 * 10^4- 题目数据保证输入的树是 完全二叉树
解题思路
解题思路
这道题的关键在于利用完全二叉树的特性来优化算法。有几种解法:
方法一:直接遍历(O(n)) 最直观的方法是遍历所有节点,但不满足题目要求的小于O(n)复杂度。
方法二:利用完全二叉树特性(推荐) 完全二叉树有一个重要性质:对于任意子树,如果左子树高度等于右子树高度,则左子树一定是满二叉树;否则右子树一定是满二叉树。
算法思路:
- 分别计算左子树的最左路径深度和右子树的最右路径深度
- 如果深度相等,说明这是一棵满二叉树,节点数为 2^h - 1
- 如果深度不等,递归计算左右子树的节点数
这种方法的时间复杂度为O(log²n),因为每层递归需要O(log n)时间计算深度,总共有O(log n)层。
方法三:二分查找 也可以通过二分查找最后一层的节点数量来解决,但实现相对复杂。
代码实现
class Solution {
public:
int countNodes(TreeNode* root) {
if (!root) return 0;
int leftDepth = getDepth(root->left);
int rightDepth = getDepth(root->right);
if (leftDepth == rightDepth) {
// 左子树是满二叉树
return (1 << leftDepth) + countNodes(root->right);
} else {
// 右子树是满二叉树
return (1 << rightDepth) + countNodes(root->left);
}
}
private:
int getDepth(TreeNode* root) {
int depth = 0;
while (root) {
depth++;
root = root->left;
}
return depth;
}
};
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
left_depth = self.get_depth(root.left)
right_depth = self.get_depth(root.right)
if left_depth == right_depth:
# 左子树是满二叉树
return (1 << left_depth) + self.countNodes(root.right)
else:
# 右子树是满二叉树
return (1 << right_depth) + self.countNodes(root.left)
def get_depth(self, root):
depth = 0
while root:
depth += 1
root = root.left
return depth
public class Solution {
public int CountNodes(TreeNode root) {
if (root == null) return 0;
int leftDepth = GetDepth(root.left);
int rightDepth = GetDepth(root.right);
if (leftDepth == rightDepth) {
// 左子树是满二叉树
return (1 << leftDepth) + CountNodes(root.right);
} else {
// 右子树是满二叉树
return (1 << rightDepth) + CountNodes(root.left);
}
}
private int GetDepth(TreeNode root) {
int depth = 0;
while (root != null) {
depth++;
root = root.left;
}
return depth;
}
}
var countNodes = function(root) {
if (!root) return 0;
const getDepth = (node) => {
let depth = 0;
while (node) {
depth++;
node = node.left;
}
return depth;
};
const leftDepth = getDepth(root.left);
const rightDepth = getDepth(root.right);
if (leftDepth === rightDepth) {
return (1 << leftDepth) + countNodes(root.right);
} else {
return (1 << rightDepth) + countNodes(root.left);
}
};
复杂度分析
| 复杂度 | 数值 |
|---|---|
| 时间复杂度 | O(log²n) |
| 空间复杂度 | O(log n) |
说明:
- 时间复杂度:每次递归需要O(log n)时间计算深度,递归深度为O(log n),总时间复杂度为O(log²n)
- 空间复杂度:递归调用栈的深度为O(log n)