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)复杂度。

方法二:利用完全二叉树特性(推荐) 完全二叉树有一个重要性质:对于任意子树,如果左子树高度等于右子树高度,则左子树一定是满二叉树;否则右子树一定是满二叉树。

算法思路:

  1. 分别计算左子树的最左路径深度和右子树的最右路径深度
  2. 如果深度相等,说明这是一棵满二叉树,节点数为 2^h - 1
  3. 如果深度不等,递归计算左右子树的节点数

这种方法的时间复杂度为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)

相关题目