题目来源:力扣
题目描述:
给出一个完全二叉树,求出该树的节点个数。
说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
审题:
此题为求树中节点个数,可以直接使用递归方法计算树中遍历树中所有节点,计算节点总个数.该方法时间复杂度为O(N),N为树的节点个数.然而由于题目中限制了树的范围,为完全二叉树,因此我们考虑充分利用这一已知条件,设计算法以取得更小的时间复杂度.
- 首先分析可以得出,由于完全二叉树除最底层叶节点外,其他层都是满的,因此我们可以通过计算树的高度来很容易计算得出除最底层外节点总个数.此时,我们考虑如何计算底层叶节点个数.
- 由于完全二叉树最下面一层的节点都集中在该层最左边的若干位置,因此,我们可以考虑使用二分搜索法计算底层叶节点个数.假设我们已经设计了方法
exist(index, d, root)
,如果以root为根的树,其第d层中从左往右的下标为index的节点存在,则返回true
.基于该方法,我们可以容易将计算底层叶节点个数转化为在有重复值条件下,搜索目标值最大位置下标问题. - 由于二叉树的特性,我们可以使用类似二分搜索的方法实现
exist(index, d, root)
函数,由于第d层最多含有 2 d − 1 2^{d-1} 2d−1个节点,因此如果 i n d e x > = 2 d − 2 index >= 2^{d-2} index>=2d−2,则index若存在,则应该位于root的右子树.
时间复杂度分析:
对于完全二叉树,可以沿左子树移动计算树高度,因此计算树高度时间复杂度为O(lgN).使用二分搜索法判断一个节点是否存在最坏情形为移动到树的最底层,因此时间复杂度为O(lgN), 由于使用二分搜索法计算底层节点个数,需要搜索lgN次,因此计算底层节点个数时间复杂度为O(lgN * lgN)
java 算法实现:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
//计算完全二叉树高度
private int height(TreeNode root){
if(root == null)
return 0;
int height = 0;
while(root != null){
root = root.left;
height++;
}
return height;
}
//判断在d层编号为index的节点是否存在
private boolean existNode(int index, int d, TreeNode root){
int lo = 0;
int hi = (int)Math.pow(2, d-1);
int step = 1;
while(step < d && root != null){
int mid = lo + (hi-lo) / 2;
if(index >= mid){//如果存在应当在后半部分
root = root.right;
lo = mid + 1;
}
else{
root = root.left;
hi = mid;
}
step++;
}
return root != null; //如果移动步数等于d-1步,则第d层编号为index的节点存在
}
public int countNodes(TreeNode root) {
int h = height(root);
if(h == 0)
return 0;
int lo = 0;
int hi = (int)Math.pow(2, h-1);
while(lo < hi){
int mid = lo + (hi-lo) / 2;
if(existNode(mid, h, root))
lo = mid+1;
else
hi = mid;
}
int bottomNum = lo;
return (int)Math.pow(2, h-1) - 1 + bottomNum;
}
}