大家好,都吃晚饭了吗?我是Kaiqisan,是一个已经走出社恐的一般生徒,今天讲讲二叉树中比较难的一道题—求二叉树的节点个数
首先还是明确一下概念----何为完全二叉树?
一棵树的节点自上而下,自左而右排列没有空缺的一颗树叫做完全二叉树,设二叉树的深度为k,除第 k 层外,其它各层 (1~k-1) 的结点数都达到最大个数,第k 层所有的结点都连续集中在最左边,这就是完全二叉树。
PS:完全二叉树的特点,完全二叉树的所有子树都是完全二叉树
完全二叉树,因为456从左到右连续
1
/ \
2 3
/ \ /
4 5 6
不是完全二叉树因为456从左到右不连续
1
/ \
2 3
/ \ \
4 5 6
不是完全二叉树因为第二层没有满
1
/
2
/ \
4 5
问题
求一个完全二叉树的节点个数
进阶要求 :时间复杂度为 O(n) – (暗示不能逐个遍历元素)
思路
先从根节点开始往左无脑遍历(遍历路线1248),先找出该二叉树的最大深度(图中为4)
然后指针撇到它的右孩子(图中从1 -> 3),然后从以3为根节点的子树开始无脑往左遍历(遍历路线为3 6 12)
此时,发现此时12所在的层数为二叉树的最大深度,所以这可以证明以2为根节点的二叉树为满二叉树,又因为确定层数,所以,以二为根节点的二叉树的个数已经确定
然后,我们撇去根节点1 以及以2为根节点的子树
此时已统计的节点达到 2^(4 - 1) = 8个
然后开始新的统计,从根节点3开始无脑向左遍历(遍历路线 3 6 12),深度为3
然后又向右子树遍历,重复上面的步骤,发现无法遍历到最大的深度
然后,我们撇去根节点13以及以7为根节点的子树
此时统计的节点数达到 8 + 2^( 3 - 1 -1) = 10 个
然后重复上面的步骤
最终所有的节点遍历结束,一共遍历了12个节点
可能上面的案例在子树这里的数据规模没有那么大了,可能对大家的理解上造成一定的阻碍,我们重新给出两个子树,大家可以进行平行比较!
tree1
tree2
这两棵树从根节点开始无脑遍历,它们的层数都是4,然后它们往右遍历(先查看右子树,然后从这个右子树开始无脑往左孩子遍历)的时候是不同的
tree2
通过遍历路线 3 6 12 可以遍历到最底层,这显然以2
为根节点的子树(1的左孩子)就是满二叉树
tree1
无法遍历到根节点
所以就可以证明以3
为根节点的子树(1的右孩子)就是满二叉树
代码
// 主函数
static int doSum(Tree head) {
if (head == null) {
return 0;
}
return exeSum(head, 1, findMostLeftLevel(head, 1));
}
static int exeSum(Tree head, int level, int hei) {
// 遍历到最底层,只有一个元素了
if (level == hei) {
return 1;
}
if (findMostLeftLevel(head.right, level + 1) == hei) {
return (1 << hei - level) + exeSum(head.right, level + 1, hei); // <<是位运算符,等同于2的平方
} else {
return (1 << (hei - level - 1)) + exeSum(head.left, level + 1, hei);
}
}
static int findMostLeftLevel(Tree head, int level) {
while (head != null) {
head = head.left;
level++;
}
return level - 1;
}
总结
上面的程序的时间复杂度为 O(lg (n))
记得记住思路熬!