生活,是一种缓缓如夏日流水般的前进,我们不要焦急。
给你一棵完全二叉树的根节点 root ,求出该树的节点个数。完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
题目链接:https://leetcode-cn.com/problems/count-complete-tree-nodes/
对于任意二叉树,我们都可以使用广度优先搜索或深度优先搜索来计算节点的个数,时间复杂度和空间复杂度都是 O(n)。节点个数等于左子树个数+右子树个数+根结点个数。我们就可以得出如下代码
动图演示如下:
时间复杂度:O(n)【不考虑递归调用栈】
空间复杂度:O(n)
class Solution
{
public:
int countNodes(TreeNode* root)
{
if(root==nullptr) //如果为空,返回0
{
return 0;
}
//左子树个数+右子树个数+根结点
return countNodes(root->left)+countNodes(root->right)+1;
}
};
得出上述方法后,我们想想还有没有更优的方法呢?题目给的条件我们是否有全部用到呢?
这里我们发现题目中的一个题眼,那就是这个二叉树不是普通的二叉树,而是完全二叉树。
那么完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
我们知道完全二叉树最后一层节点的个数范围是在1~2^(k-1)之间。那么我们就可以利用满二叉树与完全二叉树的性质来优化代码。
针对情况一:若发现它的子树部分是满二叉树,直接返回该子树的结点个数 。结点个数等于2^(k-1)。
针对情况二:分别递归左、右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。
动画演示如下:
根据上面的情况,我们可以写出如下方法二的解法。
时间复杂度:O(logn * logn) 【不考虑递归调用栈】
空间复杂度:O(logn)
class Solution
{
public:
int countNodes(TreeNode* root)
{
if(root==nullptr)
{
return 0;
}
int leftdepth=0; //存储最左子树深度
int rightdepth=0; //存储最右子树深度
TreeNode*left=root->left;
TreeNode*right=root->right;
while(left!=nullptr) //得到左子树的深度
{
++leftdepth;
left=left->left;
}
while(right!=nullptr) //得到右子树的深度
{
++rightdepth;
right=right->right;
}
if(leftdepth==rightdepth) //判断该子树部分是不是满二叉树,是就返回它的个数
{
return (2<<leftdepth)-1;
}
return countNodes(root->left)+countNodes(root->right)+1;
}
};
1、由于树比较复杂,每当我们使用时都要注意边界条件的检查,即检查空指针。
2、我们在遍历树的代码中一旦要高度警惕,在每一处需要访问地址的时候都要问自己这个地址有没有可能是nullptr,如果是nullptr我们应该怎么处理。