描述
题目描述
给定一棵完全二叉树的头节点head,返回这棵树的节点个数。如果完全二叉树的节点数为N,请实现时间复杂度低于O(N)的解法。
示例
输入:{1,2,3}
返回值:3知识点:完全二叉树,递归
难度:⭐⭐⭐
题解
方法一:递归
解题思路:
完全二叉树的特性:
- 完全二叉树的左右子树中至少有一颗是满二叉树,叶子结点只能出现在最下两层
- 最下层的叶子一定集中在左部连续位置,因此最深处一定在最左下角(可用来求子树深度)。如果倒数第二层出现叶子,则一定在右边且连续
- 对于子树有两种情况:
- 情况1:若是左子树高度大,则右边子树一定是满的。
- 情况2:若是左右深度相同,则左边子树一定满的


根据以上几个完全二叉树的特性,通过遍历每个所有结点,根据不同情况统计结点数即可
我们可以通过递归,获取每个结点作为根的二叉树节点个数
满二叉树计算公式:2 ^ heigh
总的结点数 = 根节点 + 满二叉树节点个数 + 完全二叉树节点个数
算法流程:
- 定义递归函数功能:获取每个结点作为根的二叉树节点个数
- 先得到当前结点head的左右子树高度,根据左右子树高度分情况:
- 情况1:左子树高度大于右子树高度,即当前结点head的右子树是满二叉树
- 总的结点数 = 根节点 + 满二叉树节点个数(右子树) + 完全二叉树节点个数(左子树)
- 情况2:左右子树高度相同,即当前结点head的左子树是满二叉树
- 总的结点数 = 根节点 + 满二叉树节点个数(左子树) + 完全二叉树节点个数(右子树)
- 情况1:左子树高度大于右子树高度,即当前结点head的右子树是满二叉树
Java 版本代码如下:
import java.util.*;
public class Solution {
// 递归函数功能:获取每个结点作为根的二叉树节点个数
public int nodeNum(TreeNode head) {
if(head == null) {
return 0;
}
// 得到当前结点head的左右子树高度
int left = getHeight(head.left);
int right = getHeight(head.right);
if(left != right) {
// 情况1:左子树高度大于右子树高度,即当前结点head的右子树是满二叉树
// 满二叉树计算公式:2 ^ heigh
// 总的结点数 = 根节点 + 满二叉树节点个数(右子树) + 完全二叉树节点个数(左子树)
return 1 + (int)Math.pow(2, right) - 1 + nodeNum(head.left);
} else {
// 情况2:左右子树高度相同,即当前结点head的左子树是满二叉树
// 总的结点数 = 根节点 + 满二叉树节点个数(左子树) + 完全二叉树节点个数(右子树)
return 1 + (int)Math.pow(2, left) - 1 + nodeNum(head.right);
}
}
// 获取当前结点的深度
public int getHeight(TreeNode root) {
int count = 0;
// 只需要计算左叶子结点深度就行
while(root != null) {
count++;
root = root.left;
}
return count;
}
} 复杂度分析:
时间复杂度 O((lgN^2)):每次到最左寻找最大深度时间为树的最大深度O(lgn),对于不满的二叉树递归下去,递归复杂度是O(lgn),相乘为O(lgn ^2)
空间复杂度 O(lgN):递归所需要栈的最大深度,对于不满的二叉树的子树需要递归
方法二:暴力解法
虽然暴力解法可以实现,但是事件复杂度不符合要求
算法流程:
- 定义递归函数功能:获取每个结点作为根的二叉树节点个数
- 递归每个结点,统计节点数
Java 版本代码如下:
import java.util.*;
public class Solution {
// 递归函数功能:获取每个结点作为根的二叉树节点个数
public int nodeNum(TreeNode head) {
if(head == null) {
return 0;
}
// 根节点非空, 算1个
int res = 1;
// 递归每个结点,递归加上左右孩子, 不用判空
res += nodeNum(head.left);
res += nodeNum(head.right);
return res;
}
} 复杂度分析:
时间复杂度 O(N):需要递归每个结点
空间复杂度 O(N):递归需要的栈空间

京公网安备 11010502036488号