LeetCode 每日一题222. 完全二叉树的节点个数

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

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

说明:

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

示例:

输入: 
    1
   / \
  2   3
 / \  /
4  5 6

输出: 6

节点类如下:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

方法一:不讲武德

对于任意二叉树,bfs 或者 dfs 扫一遍即可

时间复杂度和空间复杂度都为 O(n)。

public int countNodes(TreeNode root) {
    if (root == null){
        return 0;
    }
    int left = countNodes(root.left);
    int right = countNodes(root.right);
    return  left + right + 1;
}

方法二:二分查找 + 位运算

ps:参考官方解答,确实高招~

对于「完全二叉树」,假设层数为 h ,最后一层的节点个数在 1 到 2h 之间,对于 h >= 1,h - 1 层共有 2h - 1 个节点。

所以 h 层,节点总个数在 [ 2h, 2h+1 - 1]。设 k 为总节点个数,第 h 层最多有 2h个节点,二分查找每次淘汰一半的数据,所以只需 h 次操作便能找出 k。

具体做法是每次找出中间值 mid ,判断 mid 是否在最后一层。「在」则抛弃 mid 前面一半的数据,「不在」则抛弃 mid 后面一半的数据。

下面便是精髓,如何判断某个节点(中间值)是否在第 h 层?

如图所示(图片来自官方解答):判断第 12 个节点是否在最后一层。

12 的二进制为 1100,去除首位得到 100,1 代表右移到右节点, 0 代表左移到左节点。从根节点开始「右→左→ 左」就能找到第 12 个节点。如果该节点为 null 则总数小于当前节点,反之总数大于等于当前节点。

通过位运算,我们经过 h 次操作(根据二进制位左移或右移)就能判断某个节点是否在第 h 层。
在这里插入图片描述

public int countNodes1(TreeNode root) {
    if (root == null){
        return 0;
    }
    // 算出层数
    int level = 0;
    TreeNode tmp = root;
    while (tmp.left != null) {
        level++;
        tmp = tmp.left;
    }

    //  low:最后一层第一个节点对应的个数, high:最后一层最后一个节点对应的个数
    int low = 1 << level;
    int high = (1 << (level + 1)) - 1;

    // 通过二分查找找出目标节点
    while (low < high) {
        int mid = (high - low + 1) / 2 + low;
        if (exists(root, level, mid)) {
            low = mid;
        } else {
            high = mid - 1;
        }
    }
    return low;
}

// 根据二进制位进行左移右移,返回该节点是否存在
public boolean exists(TreeNode root, int level, int pos) {
    int bits = 1 << (level - 1);
    TreeNode tmp = root;
    while (tmp != null && bits > 0) {
        if ((bits & pos) == 0) {
            tmp = tmp.left;
        } else {
            tmp = tmp.right;
        }
        bits >>= 1;
    }
    return tmp != null;
}

总结:

算层数的时间复杂度:O(h)

二分查找的次数为 O(h),每次查找的时间复杂度为 O(h),合起来的时间复杂度为 O(h²)

时间复杂度:O(h²) + O(h),根据主定理,时间复杂度为:O(h²),即 O(log²n)
空间复杂度:O(1)

在 n 足够大时,时间复杂度要远低于 dfs 的 O(n)。

执行结果:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值