统计完全二叉树的节点数

 

目录

问题

分析

代码

复杂度


问题

给定一个完全二叉树,统计其中的节点数,要求时间复杂度低于O(n)


分析

        统计一个二叉树的节点最简单的方法当然是遍历一次,但是这样的时间复杂度是严格的O(n),不满足要求。要更快,自然要在完全二叉树的性质上下功夫。

        完全二叉树的最后一层,节点一定是从左向右紧密排列的。由此可以想到什么?完全二叉树里一定有满的子树!而满二叉树的节点个数可以由树高直接得出,是2^h-1。所以通过判断这个完全二叉树中作为满二叉树的子树,就可以在小于O(n)的时间里得出节点数目。

        从根节点出发,沿着每个节点的左孩子行进的到底的所有节点组成的路径(称为最左路)。对于一个完全二叉树的根节点,有下面两种可能:最左路达到底层,最左路不到底层。

        ① 正如前面所说的,完全二叉树的最后一层必然从左到右紧邻排列,所以如果到达完全二叉树的最底层,则说明左子树的最后一层没有null值,左子树是满二叉树,节点数为2^h-1,加上根节点,共2^h。现在,这个二叉树只剩下右子树没有被处理,问题转化为求右子树的节点数。

        ② 右子树的最左路没有到最底层,那么左子树不一定是满二叉树,但是右子树所有的叶节点一定都在第depth-1层,右子树一定是满二叉树,节点数为2^(h-1)-1,加上根节点,共2^(h-1)。这时候整个二叉树还剩下左子树没有被处理,问题转化为求左子树的节点数。(图里当时写错了,应该是不一定满,懒得改了哈哈哈哈哈哈哈哈哈)

        完全二叉树的子树一定是完全二叉树,所以每次问题被转化为更小规模,目标依然是一个完全二叉树,前面的规律依然适用,递归可以简明地解决问题。


代码

        我们需要一个函数来统计从一个节点出发的最左路的深度,这个简单,输入一个节点和它的当前深度,在到达null之前递增深度,最后把深度-1再返回。

public int DeepestLevel(Node x, int h) {
	while(x != null) {
		h++;
		x = x.left;
	}
	return h - 1;
}

        递归函数要传入当前二叉树的根节点root、这个头节点所在的层数level、整棵树的高度h。

    (当然h也可以作为全局变量引入,习惯问题。还是避免使用全局变量吧,按层遍历,多一个int开销也不大)

public int countNodes(Node root, int level, int h) {
	if(level == h) {//叶节点
		return 1;
	}
	if(DeepstLevel(root) == h) {//左子树满
		return(1 << (h - level) + countNodes(root.right, level + 1, h));//h-level为子树树高
	}else {//左子树不满
		return(1 << (h - level - 1) + countNodes(root.left, level + 1, h))
	}
}

        主函数就简单了。

public int count(Node root) {
	if(root == null) {//空树当然是0个节点
        return 0;
    }
	return countNodes(root, 1, DeepestLevel(root, 1));
}

复杂度

    在每一层上,算法只会向一个方向发展,递归算法的时间复杂度是O(logn)。DeepestLevel()的时间复杂度也是O(logn)。整个算法的复杂度是O((logn)^2)。由洛必达法则,n趋于无穷大时,(logn)^2 / n 趋于无穷小,算法复杂度低于O(n)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值