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

题目描述

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

1. 普适方法

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

2. 根据完全二叉树的性质简化遍历次数

这是一棵完全二叉树:除最后一层外,其余层全部铺满;且最后一层向左停靠

如果根节点的左子树深度等于右子树深度,则说明左子树为满二叉树
在这里插入图片描述

如果根节点的左子树深度大于右子树深度,则说明右子树为满二叉树
在这里插入图片描述

// depth为子树的深度;为了加快幂的运算速度,可以使用移位操作符
如果知道子树是满二叉树,那么就可以轻松得到该子树的节点数目:(1<<depth) - 1; 
接着我们只需要接着对另一子树递归即可
class Solution {
    public int countNodes(TreeNode root) {
        if(root == null){
           return 0;
        } 
        //注意这里只能用root.left;因为2^n正好将根节点给算进去了,如果左子树是满二叉树,那么节点总数是2^n-1,加上根节点正好为2^n
        int left = countLevel(root.left);
        //同理,用root.right;
        int right = countLevel(root.right);
        if(left == right){
            return countNodes(root.right) + (1<<left);
        }else{
            return countNodes(root.left) + (1<<right);
        }
    }
    private int countLevel(TreeNode root){
        int level = 0;
        while(root != null){//while(root.left != null)是用来计算深度-1,仅仅差了.left,只是为了告诉自己编程多么简单。
            level++;
            root = root.left;
        }
        return level;
    }
}

3. 二分查找

根据完全二叉树的性质,我们可以清楚的知道:总节点数 = 倒数第二层以上的节点数 + 最后一层的节点数
在这里插入图片描述
怎么判断最后一层的节点是否存在,比如说上图中的6。
给定最后一层某节点的位置索引 index,将他和分界线比大小,就可以判断该节点在左子树还是右子树,例如:现在查找6这个节点,索引为3,大于分界线2,所以6在右子树;对右子树重复操作即可,剩下的工作就交给迭代了~~~

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    // 求二叉树的深度
    public int countLevels(TreeNode root) {
    	int levels = 0;
    	while (root.left!=null) {
    	    root = root.left; 
            levels++;
        }
        return levels;
    }

     /*
    * 功能: 判断最后一层第index个索引是否存在
    * root: 二叉树根节点
    * index:判断最后一层索引为index的节点是否存在, 索引范围是[1, 2^depth]
    * depth:倒数第二层的深度, 这是因为满二叉树最后一层的节点数等于 2^depth
    */
    public boolean is_exist(TreeNode root, int index, int depth) {
        TreeNode node = root;
        while (depth!=0) {
            // 最后一层分界线
            int mid = ((1 << depth) >> 1);
            if (index > mid) {
                // 如果在右子树,需要更新索引值
                index -= mid;
                node = node.right;
            }
            else {
                node = node.left;
            }
            //因为已经判断出是否在左右子树了,所以--的目的是将分界线除以2。
            depth--;
        }
        return node != null;
    }

    public int countNodes(TreeNode root) {
        // 3. 二分查找
        if (root == null) return 0;
        // 倒数第二层二叉树深度
        int depth = countLevels(root);
        int start = 1, end = (1 << depth), mid = 0;
        while (start <= end) {
            mid = start + ((end - start) >> 1);
            if (is_exist(root, mid, depth)) {
                start = mid + 1;
            }else {
                end = mid - 1;
            }
        }
        // start - 1为最后一层节点数
        int ret = (1 << depth) - 1 + start - 1;
        return ret;
    }
}

还是二分查找,另一种思路。

在这里插入图片描述

12的二进制为1100,第二层第一个节点是4,二进制表示为100,所以&操作为1,这样子可以判断在左子树还是右子树,并且index值不用变化。
具体做法是求最后一层的中间节点mid,对上一层的第一个节点做&操作,如果&操作结果是0,说明在左子节点上,如果&操作为1,说明在右子节点上。
class Solution {
    public int countNodes(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int level = 0;
        TreeNode node = root;
        while (node.left != null) {
            level++;
            node = node.left;
        }
        int low = 1 << level, high = (1 << (level + 1)) - 1;
        //一定要用<=
        while (low <= high) {
            int mid = (high - low) / 2 + low;
            if (exists(root, level, mid)) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return low - 1;
    }

    public boolean exists(TreeNode root, int level, int k) {
        int bits = 1 << (level - 1);
        TreeNode node = root;
        while (node != null && bits > 0) {
        	//要加双括号
            if ((bits & k) == 0) {
                node = node.left;
            } else {
                node = node.right;
            }
            bits >>= 1;
        }
        return node != null;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值