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

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

说明:

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

示例:

输入:

     1
    / \
  2   3
 / \  /   
4  5 6

输出: 6

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/count-complete-tree-nodes

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

数节点个数完全可以用正常treewalk 代码超级简单而且O(n)的时间复杂度也不算慢:

class Solution {

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

不过这是个完全二叉树是可以优化的,自己没想出来看题解了。首先,叶节点的深度至多相差一,并且最深的一层节点是从左往右排开的。如果叶节点深度相同(即一直往左走的深度和一直往右走的深度相同),那么节点数应该为2h-1(h从1开始编号);如果深度不同,可以在叶节点上一层二分搜索出有子节点的边界,左边加上子节点,右边不加。
假设搜索一层深度是h,那该层应该有2h-1个节点。可以将0到2h-1-1的这么些数和这些节点一一对应。数同时也表示了从根节点怎么走能到达对应节点。从高位往低位,如果是0指往右走,是1指往左走,这样数+1,指向的节点也正好右移一个位置。有数找对应节点的代码如下:

TreeNode walk(TreeNode root, int mm, int height) {
    TreeNode t = root;
    for(int i = 0; i < height; ++i) {
        if((mm & (1 << (height - i - 1))) != 0) {
            t = t.right;
        } else {
            t = t.left;
        }
    }
    return t;
}

这样在0到2h-1-1中二分搜索出边界就好:

int l = 0, r = pow2 - 1;//二分搜索
while(l < r) {
    int mid = (l + r) / 2;
    t = walk(root, mid, rHeight - 1);            
    if(t.left == null) {
        r = mid - 1;
    } else if(t.right == null) {
        l = mid;
        r = mid;
    } else {
        l = mid + 1;
    }
}

对于边界,确定的是边界左边都有两个子节点,边界右边没有,对于边界需要稍微讨论一下:

if(t.left == null) {
    return 2 * l + pow2 * 2 - 1;
} else if(t.right == null) {
    return 2 * l + pow2 * 2;
} else {
    return 2 * l + pow2 * 2 + 1;
}

完整代码:

class Solution {

    TreeNode walk(TreeNode root, int mm, int height) {
        TreeNode t = root;
        for(int i = 0; i < height; ++i) {
            if((mm & (1 << (height - i - 1))) != 0) {
                t = t.right;
            } else {
                t = t.left;
            }
        }
        return t;
    }

    public int countNodes(TreeNode root) {
        if(root == null) {
            return 0;
        }

        int lHeight = 0, rHeight = 0;
        TreeNode t = root;
        int pow2 = 1;
        while(t != null) {
            t = t.left;
            ++lHeight;
            pow2 *= 2;
        }
        t = root;
        while(t != null) {
            t = t.right;
            ++rHeight;
        }
        if(lHeight == rHeight) {
            return pow2 - 1;
        }
        pow2 /= 4;
        int l = 0, r = pow2 - 1;//二分搜索
        while(l < r) {
            int mid = (l + r) / 2;
            t = walk(root, mid, rHeight - 1);            
            if(t.left == null) {
                r = mid - 1;
            } else if(t.right == null) {
                l = mid;
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        t = walk(root, l, rHeight - 1);

        if(t.left == null) {
            return 2 * l + pow2 * 2 - 1;
        } else if(t.right == null) {
            return 2 * l + pow2 * 2;
        } else {
            return 2 * l + pow2 * 2 + 1;
        }
    }
}

这样的事件复杂度是logn * logn,每次walk需要logn,二分搜索到边界需要walk lgn次

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值