完全二叉树节点个数

完全二叉树节点个数

最简单的解法就是遍历这棵树,时间复杂度是O(N),如果只是这一种解法,就不会有本文了。本文中给出两种解法。

完全二叉树

第一次看到完全二叉树的定义比较懵,搜索到的定义如下:

设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。

谁到不想看这个定义,比较简单的理解就是和满二叉树相比较,看按层次遍历的标号是否一致。具体方案不再阐述,网上都有。

实现

  • 遍历
  • 借助完全二叉树的特点
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

遍历

看一下递归的公式:

  • 空树:返回0
  • 非空树:递归的统计左右子树的节点个数,返回左右子树节点个数加1的和
func countNodes(root *TreeNode) int {
    if root == nil {
        return 0
    }
    
    sumL := countNodes(root.Left)
    sumR := countNodes(root.Right)
    
    return sumL+sumR+1
}

借助完全二叉树的特点

这个特点是什么呢?完全二叉树=满二叉树+完全二叉树。完全二叉树里藏着一颗满二叉树和另外一颗完全二叉树,最惊喜的是从公式里看出子问题和原问题基本一致,可以使用递归啊。

完全二叉树的层数

这种算法的实现需要知道完全二叉树的层数,难道又需要遍历统计吗,当然不需要了,还是借助完全二叉树的特点,可以根据完全二叉树的最左节点和一个计数变量搞定,因为这个最左节点肯定是位于最后一层。

func height(root *TreeNode) int {
    h := 0
    
    for root != nil {
        h++
        root = root.Left
    }
    
    return h
}
原理
  • h = height(root), hright = height(root.Right)
  • 左子树和右子树其中有一颗是完全二叉树,另一颗是满二叉树
  • 区别左右子树的种类根据h-hright
    • h-hright=1:
      • 左子树:满二叉树
      • 右子树:完全二叉树
    • h-hright=2:
      • 左子树:完全二叉树
      • 右子树:满二叉树
  • 节点个数=1+左子树节点个数+右子树节点个数 = (1 << hright)+递归另一颗完全二叉树(满二叉树 = 1 << hright)
第一种情况

image

第二种情况

image

实现
func countNodes(root *TreeNode) int {
    if root == nil {
        return 0
    }
    
    return nodeNums(root)
}

func nodeNums(root *TreeNode) int {
    h := height(root)
    hR := height(root.Right)
    
    //两种情况满二叉树层数都是hR
    //不同在于递归左子树还是右子树
    nums := 1 << uint(hR)
    if hR+1 == h {
        return nums+countNodes(root.Right)
    }
    
    return nums+countNodes(root.Left)
}

func height(root *TreeNode) int {
    h := 0
    
    for root != nil {
        h++
        root = root.Left
    }
    
    return h
}
时间复杂度

对于每一个节点最多需要访问深度个数的节点,所以最终时间复杂度是O(lg2*lg2)。

关于作者

大四学生一枚,分析数据结构,面试题,golang,C语言等知识。QQ交流群:521625004。微信公众号:后台技术栈。
image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值