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

先序

递归一下就出来了

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

BFS

input是一个完全二叉树,可以考虑用完全二叉树的性质来做
显然如果某个结点没有右孩子而有左孩子,或者左右孩子都没有,那么这个结点肯定属于这棵树的第h - 1 层,假设这棵树高为h,则共有结点 2 h − 1 2^h-1 2h1个,我们可以统计出前h-1层二叉树的总结点数是 2 h − 1 − 1 2^{h-1}-1 2h11
然后统计下一层的叶子节点数即可

我们用BFS来遍历,遍历过程中不断统计下一层的结点数,只要我们碰到第一个没有右孩子的结点,我们就可以直接可以得到结果

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if not root:
            return 0
        que = [root]
        h = 1
        cnt = 0
        while que:
            cnt = 0
            size = len(que)
            for i in range(size):
                front = que.pop(0)
                if front.left:
                    que.append(front.left)
                    cnt += 1
                if not front.right:
                    return 2**(h)-1+cnt
                if front.right:
                    que.append(front.right)
                    cnt += 1
            h+=1

但是这还是太慢了,因为比起递归,仍然遍历前h-1层的大部分节点,并没有得到什么改进

利用性质的递归

考虑到一颗完全二叉树叶子结点只会出现在最后两层,那么一共两种形态
假设树高为h,左右子树的树高分别为left.h和right.h

  1. left.h>right.h
    如图(图片来自https://leetcode-cn.com/problems/count-complete-tree-nodes/solution/c-san-chong-fang-fa-jie-jue-wan-quan-er-cha-shu-de/)
    在这里插入图片描述
    此时右子树是一颗满二叉树,右子树的结点数为 2 r i g h t . h − 1 2^{right.h}-1 2right.h1

  2. left.h == right.h ,此时左子树除了最后一层是一颗满二叉树
    在这里插入图片描述
    此时左子树是一颗满二叉树,左子树的结点数为 2 l e f t . h − 1 2^{left.h}-1 2left.h1

那么由此我们可以写出递归代码

def func(root):
	if not root:
		return 0
	统计左右子树树高
	if lh==rh:
		# 此时树能够拆分成一颗满二叉树加上一个根节点和一颗完全二叉树,我们处理剩下的完全二叉树即可
		return 2^{lh} + func(root.right)
	else:
		return 2^{rh} + func(root.left)

那么我们还需要求左右子树的高度,在这题中,一个巧妙的做法是,我们沿着左右子树的左孩子一直走就能得到它们各自的深度

class Solution:
    def countNodes(self, root: TreeNode) -> int:
        if not root:
            return 0
        lh = self.getHigh(root.left)
        rh = self.getHigh(root.right)
        if lh == rh:
            return 2**lh + self.countNodes(root.right)
        else:
            return 2**rh + self.countNodes(root.left)
        
    
    def getHigh(self,root):
        if not root:
            return 0
        return self.getHigh(root.left) +1

这个版本的算法就快很多了,时间复杂度是 O ( l o g n ∗ l o g n ) O(logn*logn) O(lognlogn) 可以用主定理证明

T(n) = T(n/2) + lgn
其中lgn的操作是求树高,T(n/2)是求子树的解
对应主定理第二种情况,算导第三版的P54

注意到上述解法还有优化的余地,我们可以开O(n)的空间来保存树高信息,避免迭代的过程中重复计算,或者我们将上面的递归改成迭代的版本

二分+bit

明天再写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值