力扣每日一题--完全二叉树的节点个数(中等)(最终执行用时超越99.27%)

题目描述

给出一个完全二叉树 ,求出节点个数
在这里插入图片描述

基础概念补充(完全二叉树)

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

输入输出示例

在这里插入图片描述

题解

思路一

这个题目,其实我个人感觉不像是中等难度的题目,因为,确实过于简单
第一个想法就是,先判断节点是否为叶子节点,如果是,就返回1(只有自己一个节点)。如果不是,说明它有孩子节点,那么接下来判断,是否有右子树。如果没有,则左子树,返回1+count(left),如果有则返回1+count(left)+count(right)。
最后加一个特殊判断,就是如果root是空的,那么返回0

代码一

class Solution:
    def is_leaf(self,node:TreeNode):
        if ((node.left==None) and (node.right==None)):
            return True
        else:
            return False
    
    def countNodes(self, root: TreeNode) -> int:
        if (root==None):
            return 0
        else:
            if (self.is_leaf(root)):
                return 1
            else:
                if root.right==None:
                    return 1+self.countNodes(root.left)
                else:
                    return 1+self.countNodes(root.left)+self.countNodes(root.right)

运行结果一

在这里插入图片描述
显然,还有很多需要改进的地方


思路二

后来想了想,其实没必要那么麻烦判断是否有子树,直接count就行了,如果是空返回0就好了,这样应该能简化不少

代码二

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if (root==None):
            return 0
        else:
            return 1+self.countNodes(root.left)+self.countNodes(root.right)

运行结果二

在这里插入图片描述
相比上次是有所改进,但是好像还是不够好,因为到这里还有一个未用到的条件


思路三(LeetCode官方思路)

之前提到过的未用到的条件 ,就是题目中的完全二叉树 。之前我们的算法是使用所有的二叉树的,所以必然有一些不太必要的操作,所以这次我们要利用完全二叉树的特点进行计算。

规定根节点位于第 0 层,完全二叉树的最大层数为 h。根据完全二叉树的特性可知,完全二叉树的最左边的节点一定位于最底层,因此从根节点出发,每次访问左子节点,直到遇到叶子节点,该叶子节点即为完全二叉树的最左边的节点,经过的路径长度即为最大层数 h。

非底层的第 i i i层,一共有 2 i 2^i 2i个节点。
底层的第 h h h层,可能有 [ 1 , 2 h ] [1,2^h] [1,2h]个节点,可以在该范围内通过二分查找的方式得到完全二叉树的节点个数。

h层完全二叉树所以一共可能的节点个数为 [ 2 h , 2 h + 1 − 1 ] [2^{h},2^{h+1}-1] [2h,2h+11]

具体做法是,根据节点个数范围的上下界得到当前需要判断的节点个数 k,如果第 k 个节点存在,则节点个数一定大于或等于 k,如果第 k 个节点不存在,则节点个数一定小于 k,由此可以将查找的范围缩小一半,直到得到节点个数。

如何判断第 k 个节点是否存在呢?如果第 k 个节点位于第 h 层,则 k 的二进制表示包含 h+1 位,其中最高位是 1,其余各位从高到低表示从根节点到第 k 个节点的路径,0 表示移动到左子节点,1 表示移动到右子节点。通过位运算得到第 k 个节点对应的路径,判断该路径对应的节点是否存在,即可判断第 k 个节点是否存在。

代码三(自己写的代码,官方没给)

# 这里注明一下,官方并未给出python3的解答,我是自己根据官方的思路写的代码
class Solution:
    def check_exist(self,root,k):
        string_bin_k = str(bin(k))
        for i in range(3,len(string_bin_k)):
            print(string_bin_k[i])
            if string_bin_k[i] == '0':
                root = root.left
            else:
                root = root.right
        if root == None:
            return False
        else:
            return True

    def get_height(self,root):
        if root == None:
            return 0
        height = -1
        while (True):
            height += 1
            root = root.left
            if (root == None):
                break
        return height

    def bin_search(self,min,max,root):
        if min==max-1:
            if self.check_exist(root,max):
                return max
            else:
                return min
        else:
            k=(min+max)//2
            if self.check_exist(root,k):
                return self.bin_search(k,max,root)
            else:
                return self.bin_search(min,k,root)

    def countNodes(self, root: TreeNode) -> int:
        if root == None:
            return 0
        else:
            height = self.get_height(root)
            if height == 0:
                return 1
            else:
                return self.bin_search(pow(2,height),pow(2,height+1)-1,root)

运行结果三

本以为如此复杂的逻辑会导致运行结果不理想,其实,比之前的二结果要好
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值