完全二叉树节点问题
一. 简介
首先,明确一下完全二叉树和满二叉树的区别。
下图是一颗完全二叉树,它的每一层都是紧凑靠左排列的:
满二叉树则是一种特殊的完全二叉树,每层都是满的:
计算一颗完全二叉树的节点个数,算法的时间复杂度应该为(logN*logN)。
二.如何求一颗完全二叉树节点个数
// 输入一棵完全二叉树,返回节点总数
int countNodes(TreeNode root);
普通的二叉树的话,很简单:
int countNodes(TreeNode root){
if(root ==null) return 0;
return 1+countNodes(root.left)+countNodes(root.right);
}
如果是一颗满二叉树,节点总数和树的高度呈指数关系,时间复杂度为O(logN):
public int countNodes(TreeNode root){
int h=0;
while(root!=null){
root = root.left;
h++;
}
return (int)Math.pow(2,h)-1; //这里需要转换是因为Math.pow方法得到的是double类型。
}
完全二叉树是普通二叉树和满二叉树的结合:
public int countNodes(TreeNode root){
TreeNode l=root,r=root;
int hl=0,hr=0;
while(l!=null){
l=l.left;
hl++;
}
while(r!=null){
r=r.left;
hr++;
}
//左右子树高度一样,按满二叉树计算
if(hl==hr) return (int)Math.pow(2,hl)-1;
else return 1+countNodes(root.left)+countNodes(root.right);
}
三.复杂度分析
这个算法的时间复杂度是O(logNlogN);但很容易认为算法的复杂度是O(NlogN),因为之前的while需要logN的时间,最后要O(N)的时间向左右子树递归:return 1+countNodes(root.left)+countNodes(root.right)
然而,关键点在于,这两个递归实际上只有一个会真的递归下去,另一个一定会触发hl=hr而立刻返回。
因为一颗完全二叉树的两棵子树,至少有一棵是满二叉树。
「完全二叉树」这个概念还是有它存在的原因的,不仅适用于数组实现二叉堆,而且连计算节点总数这种看起来简单的操作都有高效的算法实现。