java数据结构与算法刷题-----LeetCode222. 完全二叉树的节点个数

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846

在这里插入图片描述

1. 法一:利用完全二叉树性质,进行递归二分查找

解题思路:时间复杂度O( l o g 2 n log_2{n} log2n),空间复杂度O(n),因为递归底层需要栈来实现
  1. 完全二叉树:除去最后一层,剩下的是满二叉树。其中满二叉树也是完全二叉树的一种。
  1. 完全二叉树最后一层的叶子结点,必须是连续的
  2. 最后一层如果不满,假设最后一层只有一个叶子结点,也一定是左结点。因为它必须连续。所以,缺也不会缺左结点
  1. 满二叉树的结点个数为: 2 高度 − 1 2^{高度}-1 2高度1,例如一个满二叉树高的为5,那么一共有 2 5 − 1 = 31 2^5-1 = 31 251=31个结点
  2. 所以,我们依次从根节点开始遍历
  1. 如果当前结点代表着一棵满二叉树(完全二叉树前提下,左子树高的 = 右子树高度),那么代表左子树一定是满的。而右子树未必是满的,因为只是高度一样,完全二叉树只能保证,缺也不会缺左结点。而现在有右节点,说明左节点不缺
  2. 因此左子树高的 = 右子树高度,可以直接跳过左子树,用公式 2 左子树高度 − 1 2^{左子树高度}-1 2左子树高度1计算左子树的结点个数,然后算上当前根节点+1,最终为 2 左子树高度 2^{左子树高度} 2左子树高度个结点
  3. 但是如果左子树高的 != 右子树高度,说明最后一层不满,但是除去最后一层,其余的一定为满二叉树,所以右子树一定是满二叉树,只不过比左子树高的少1而已
  4. 因此左子树高的 != 右子树高度时:右子树的结点个数+当前结点 = 2 右子树高度 2^{右子树高度} 2右子树高度
代码

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    /**
        left == right。表示左右子树深度相同,又因为是完全二叉树,因为节点已经填充到右子树了
        所以左子树一定是满二叉树,所以左子树的节点总数是 2^left - 1,加上当前这个 root 节点,则正好是 2^left(2的left次方)。
        然后只需要再对右子树进行递归统计即可。
        left != right。表示左右子树深度并不一致,因为是完全二叉树,必然是左深右浅
        而且最后一层是不满的,倒数第二层必然是满的,而左深右浅,说明右子树是满二叉树,它的深度比左子树少1。为2^right
        那么也同样进行,右子树节点 + root 节点,总数为 2^right。再对左子树进行递归查找。
        
        其中2的多少多少次方,可以写为 1 << 多少多少。例如 1 << left等价于2^left(2的left次方)
     */
    public int countNodes(TreeNode root) {
        if(root == null) return 0;//如果是空树直接返回0
        int left = countLevel(root.left);//获取左子树的深度
        int right = countLevel(root.right);//获取右子树的深度
        //如果左子树深度和右子树一致,就说明root的子树是满二叉树
        if(left == right)//如果是满二叉树,它的最后一个叶子结点一定在最右边侧
            return countNodes(root.right) + (1 << left);//所以疯狂递归统计右边结点,另外不要忘记加上左子树的结点
        else//如果不是满二叉树,那么对完全二叉树来说,它的最后一个叶子结点一定在左侧
            return countNodes(root.left) + (1 << right);//所以疯狂遍历左边,另外不要忘记加上右子树结点
    }
    //统计root结点的深度
    private int countLevel(TreeNode root){
        int level = 0;
        //只统计左子树,因为是完全二叉树的原因,无论是否为满二叉树,哪怕是它的最后一层只有一个叶子结点
        //也一定落在左侧。
        while(root != null){
            level++;
            root = root.left;//每向下走一次,level + 1 一下
        }
        return level;
    }
}

2. 二分查找+位运算

解题思路:时间复杂度O( l o g 2 n log_2{n} log2n),空间复杂度O(1)
  1. 先统计深度
  2. 那么倒数第二层必然是满二叉树。
  3. 最后一层不确定有多少个
  4. 我们对其进行二分查找。看看最后一个叶子结点,在最后一层哪个位置
  5. 最后将倒数第二层的满二叉树+最后一层的结点个数 = 整个数的结点个数
代码

在这里插入图片描述

class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) {//如果root为null直接返回
            return 0;
        }
        int level = 0;//保存高度(深度)
        TreeNode node = root;//获取当前结点
        while (node.left != null) {//如果左节点存在
            level++;//统计深度
            node = node.left;
        }
        //low表示左后一层最左边的结点。
        //因为我们从0开始统计的level,所以1 << level -1(2^level-1)正好是倒数第二层的满二叉树结点
        //high 表示 如果是满二叉树,它为最后一个结点
        //我们在这个范围进行二分查找
        int low = 1 << level, 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 k) {
        int bits = 1 << (level - 1);//bits指向倒数第二层
        TreeNode node = root;
        while (node != null && bits > 0) {
            if ((bits & k) == 0) {
                node = node.left;
            } else {
                node = node.right;
            }
            bits >>= 1;
        }
        return node != null;
    }
}

  • 56
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殷丿grd_志鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值