222. 完全二叉树的节点个数 - 力扣(LeetCode)
自顶向下遍历
class Solution {
int sum; //注意sum是全局变量,根节点和孩子节点都能共享。孩子节点直接把计数加在sum上,而不传回给父亲节点。
public:
//root先把自己计数一次,再把任务交给孩子节点,让它们自己去把自己以及孩子的孩子节点们加到sum进行计数
int countNodes(TreeNode* root) {
if(root==nullptr) return 0;
sum++;
countNodes(root->left);
countNodes(root->right);
return sum;
}
};
自底向上 递归遍历
class Solution {
public:
//root先把任务分发给孩子节点,让它们分别统计子树节点个数,再通过return传回给父节点,父节点再把自己进行计数。
int countNodes(TreeNode* root) {
if(root==nullptr) return 0;
int sum=0; //这个sum负责统计当前节点以及它孩子节点的个数,父亲节点看不到这个sum。
sum += countNodes(root->left);
sum += countNodes(root->right);
sum++;
return sum;
}
};
后序遍历
class Solution {
public:
int countNodes(TreeNode* root) {
//root先把任务分发给孩子节点,让它们分别统计子树节点个数,再通过return传回给父节点,父节点再把自己进行计数。
if(root==nullptr)return 0;
int leftheight=countNodes(root->left); //左
int rightheight=countNodes(root->right); //右
int rootheight=1; //根
return leftheight+rightheight+rootheight;
}
};
因为不管是前中后序遍历都需要把整个二叉树遍历一遍,时间复杂度为O(n),如果想提高效率就要利用完全二叉树的特性。
优化
示例1的左子树是一个节点全满的完全二叉树,它的特性是左孩子高度和右孩子高度相等。
并且可以利用树高直接求出完全二叉树节点个数:
所以我们先判断,如果左子树是完全二叉树,那么左子树就不用遍历了,直接用 公式求出节点个数,这样就省了效率。
那么求树高也是需要遍历的,我们这里遍历只遍历外侧,不用遍历全部节点。例如图(a-1):
(a-1)的左子树是节点全满的完全二叉树,根据绿色路径就可以求出左子树的高度,蓝色路径就不用走了。
code
class Solution {
public:
int countNodes(TreeNode* root) {
if(root==nullptr)return 0;
//判断是否是完全二叉树。
//遍历左子树
TreeNode* l=root->left;
TreeNode* r=root->right;
int lh=0,rh=0;
//统计树高
while(l)
{
l=l->left;
lh++;
}
while(r)
{
r=r->right;
rh++;
}
//如果是节点全满的完全二叉树,就用特性求
if(lh==rh){return (2<<lh)-1;}
//单层搜索逻辑
int leftnum=countNodes(root->left); //左
int rightnum=countNodes(root->right); //右
int result=leftnum+rightnum+1; //根
return result;
}
};