完全二叉树
我们先来回顾一下什么是完全二叉树?
一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。 ————来自维基百科
从定义里面,我们就知道完全二叉树其对应的结点编号和对应的满二叉树是一致的。进而我们知道它的子树必然包含一颗满二叉树子树。
计算完全二叉树的节点数(算法和优化复杂度)
1、使用普通的二叉树节点数计算
int countNode(TreeNode root){
if(root==null) return 0;
return 1+countNode(root.left)+countNode(root.right);
}
时间复杂度上看,计算了所有的结点,所以复杂度为O(n)
2、计算完美二叉树(满二叉树)的节点数
因为完美二叉树它的节点数是有规律的,每一层和下一层是成比例增加的,所以我们只需要知道其深度,即可利用等比数列的求和公式返回它的节点数。
int countNode(TreeNode root){
if(root==null) return 0;
//计算树高
int h=0;
while(root!=null){
root = root.left;
h++;
}
return (int)Math.pow(2,h)-1;
}
从时间复杂度上看,计算树高,为O(logN)
2.3、结合以上两类(完全二叉树含满二叉子树)
void countNode(TreeNode root){
if(root==null) return 0;
//计算左右子树的高度
int hl=0,hr=0;
TreeNode left = root,right=root;
while(left !=null){
left = left .left;
hl++;
}
while(right!=null){
right= right.left;
hr++;
}
if(hr==hl){//满二叉树
return (int)Math.pow(2,hl)-1;
}
//否则不是满二叉树,则按照普通的方法计算
return 1+countNode(root.left)+countNode(root.right);
}
复杂度分析上看,首先根据完全二叉树必然含有一个满二叉树,要么是左子树,要么是右子树。那么
return 1 + countNodes(root.left) + countNodes(root.right);
这两个递归只有一个会真的递归下去,另一个一定会触发hl == hr而立即返回,不会递归下去。
综上,算法的递归深度就是树的高度 O(logN),每次递归所花费的时间就是 while 循环,需要 O(logN),所以总体的时间复杂度是 O(logN*logN)。