259 字 1 分钟阅读

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例 1:

img

输入:root = [1,2,3,4,5,6]
输出:6

示例 2:

输入:root = []
输出:0

示例 3:

输入:root = [1]
输出:1

提示:

  • 树中节点的数目范围是[0, 5 * 104]
  • 0 <= Node.val <= 5 * 104
  • 题目数据保证输入的树是 完全二叉树

进阶:遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?

思路

最简单遍历一遍,每遇到一个节点 +1 即可。但是时间复杂度比较高。

可以利用完全二叉树中左右子树至少有一棵为满二叉树的特性。如果满二叉树子树高度为 h,那么该子树的节点数量为 2h - 1,可以利用该特性无需遍历即可获取一颗子树节点的数据量。

如何知道哪棵子树为完全二叉树呢?可以获取左右子树的最右节点深度 leftDepth 和 rightDepth,如果 leftDepth > rightDepth,那么左子树为满二叉树,如果 leftDepth = rightDepth 则右边为满二叉树,由于完全二叉树的特性不会出现 leftDepth < rightDepth 的情况。

222.完全二叉树的节点个数1

解法

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDepth = seekRightMostNodeDepth(root.left);
        int rightDepth = seekRightMostNodeDepth(root.right);
        
        if (leftDepth > rightDepth) {
            //左子树为满二叉树,节点数量为 2^leftDepth - 1,加上右子树节点数量,要再 +1,为头结点
            int leftCount = 1 << leftDepth;
            int rightCount = countNodes(root.right);
            return leftCount + rightCount;
        } else {
            //右子树为满二叉树,节点数量为 2^rightDepth - 1,加上左子树节点数量,要再 +1,为头结点
            int leftCount = countNodes(root.left);
            int rightCount = 1 << rightDepth;
            return leftCount + rightCount;
        }
    }

    /**
     * 获取当前子树最右节点所在深度
     */
    public int seekRightMostNodeDepth(TreeNode curRoot) {
        int depth = 0;
        while (curRoot != null) {
            curRoot = curRoot.right;
            depth++;
        }
        return depth;
    }    
}

优化写法

class Solution {
    /**
     * 针对完全二叉树的解法
     *
     * 满二叉树的结点数为:2^depth - 1
     */
    public int countNodes(TreeNode root) {
        if (root == null) return 0;
        TreeNode left = root.left;
        TreeNode right = root.right;
        int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
        while (left != null) {  // 求左子树深度
            left = left.left;
            leftDepth++;
        }
        while (right != null) { // 求右子树深度
            right = right.right;
            rightDepth++;
        }
        if (leftDepth == rightDepth) {
            return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0
        }
        return countNodes(root.left) + countNodes(root.right) + 1;
    }
}