当前位置: 首页 > news >正文

代码随想录|二叉树|11完全二叉树的节点个数

leetcode:222. 完全二叉树的节点个数 - 力扣(LeetCode)

题目

给出一个完全二叉树,求出该树的节点个数。

示例 1:

  • 输入:root = [1,2,3,4,5,6]
  • 输出:6

示例 2:

  • 输入:root = []
  • 输出:0

示例 3:

  • 输入:root = [1]
  • 输出:1

提示:

  • 树中节点的数目范围是[0, 5 * 10^4]
  • 0 <= Node.val <= 5 * 10^4
  • 题目数据保证输入的树是 完全二叉树

思路

我们分别来看普通二叉树的解法和完全二叉树的解法。

普通二叉树 

递归法很简单,对于一个节点,求其左子树节点数量+求其右子树节点数量+其本身所占1个节点=以这个节点为树根节点的树节点个数,从描述中看出后序遍历是比较好写的,代码如下:

// --------------------------普通二叉树节点个数,递归法--------------------------
class Solution
{
    /**
     * 计算以给定根节点为基础的二叉树的节点数量
     * 
     * @param root 二叉树的根节点指针
     * @return 返回二叉树的节点数量
     */
    private:
    int getNodeNum(TreeNode *root)
    {
        // 如果根节点为空,说明树不存在,返回0
        if(root==NULL)
            return 0;
        // 递归计算左子树的节点数量
        int leftNum=getNodeNum(root->left);
        // 递归计算右子树的节点数量
        int rightNum=getNodeNum(root->right);
        // 当前树的节点数量为左子树节点数、右子树节点数加上根节点,共计左子树节点数+右子树节点数+1
        int treeNum=leftNum+rightNum+1;
        // 返回当前树的节点数量
        return treeNum;
    }
    /**
     * 计算二叉树的节点数量的公共接口
     * 
     * @param root 二叉树的根节点指针
     * @return 返回二叉树的节点数量
     */
    public:
    int countNodes(TreeNode *root)
    {
        // 调用私有方法计算节点数量并返回结果
        return getNodeNum(root);
    }
};

也可以不用再定义getNodeNum函数,直接在public里面的函数操作,然后精简一下代码:

// --------------------------普通二叉树节点个数,递归法(精简版本)--------------------------
class Solution
{
public:
    int countNodes(TreeNode *root)
    {
        if (root == NULL)
            return 0;
        return 1 + countNodes(root->left) + countNodes(root->right);
    }
};

以上是递归法,我们前面在用迭代法的层序遍历时,使用队列记录了每一层的元素,这里只需要稍微修改模板就可以求节点数量了,代码如下:

// --------------------------普通二叉树节点个数,迭代法层序遍历--------------------------
class Solution {
    public:
        int countNodes(TreeNode* root) {
            queue<TreeNode*> que;
            int result=0;
            if(root!=NULL)  que.push(root);
            while(!que.empty())
            {
                int size=que.size();
                while(size)
                {
                    TreeNode *node=que.front();
                    que.pop();
                    result++;
                    if(node->left)  que.push(node->left);
                    if(node->right) que.push(node->right);
                    size--;
                }
            }
            return result;
        }
    };

完全二叉树

以上方法对于普通二叉树都可以解决,对于完全二叉树,他自己也有特性。

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1)  个节点。

 

 

完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。

对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。

对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。

完全二叉树(一)如图:

 完全二叉树(二)如图:

可以看出如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量。

如何去判断一个左子树或者右子树是不是满二叉树呢?

在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。如图:

在完全二叉树中,如果递归向左遍历的深度不等于递归向右遍历的深度,则说明不是满二叉树,如图:

判断其子树是不是满二叉树,如果是则利用公式计算这个子树(满二叉树)的节点数量,如果不是则继续递归。

利用完全二叉树特性计算节点数量的代码如下:

// --------------------------完全二叉树求节点个数--------------------------
class Solution
{
public:
    /**
     * 计算二叉树的节点数
     * @param root 二叉树的根节点指针
     * @return 返回二叉树的节点总数
     */
    int countNodes(TreeNode *root)
    {
        // 如果根节点为空,说明树不存在,返回0
        if (root == NULL)
            return 0;
        
        // 分别获取根节点的左子树和右子树
        TreeNode *left = root->left;
        TreeNode *right = root->right;
        
        // 初始化左子树和右子树的深度
        int leftDepth = 0, rightDepth = 0;
        
        // 计算左子树的深度
        while (left)
        {
            left = left->left;
            leftDepth++;
        }
        
        // 计算右子树的深度
        while (right)
        {
            right = right->right;
            rightDepth++;
        }
        
        // 如果左右子树深度相同,说明最后一层节点数相同,可以使用满二叉树的性质计算总节点数
        if (leftDepth == rightDepth)
        {
            return (2 << leftDepth) - 1;
        }
        
        // 如果左右子树深度不同,说明最后一层节点数不同,需要分别计算左子树和右子树的节点数,再加上根节点
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

总结

满二叉树一定是完全二叉树,所以完全二叉树里面的满二叉树部分可以利用公式来计算节点数量。

参考资料

代码随想录 

要理解普通二叉树和完全二叉树的区别! | LeetCode:222.完全二叉树节点的数量_哔哩哔哩_bilibili 

相关文章:

  • spring-ai-alibaba-examples项目编译运行
  • 代码随想录算法训练营第七天|组合、组合总和III和电话号码的字母组合
  • 基于cat1的贵重物品的状态和位置小型监控系统特色解析
  • 第十五届蓝桥杯C/C++B组拔河问题详解
  • OrioleDB: 新一代PostgreSQL存储引擎
  • stl之string的详解
  • 基于云的内容中台驱动企业智能服务升级
  • 并发编程--具名管道
  • HarmonyOS-应用程序框架基础
  • 应用于电池模块的 Fluent 共轭传热耦合
  • 【源码分析】Nacos服务注册源码分析-客户端
  • 解决load()文件报错zipfile.BadZipFile: File is not a zip file
  • 给easygui添加字体设置功能(tyysoft增强版)
  • WEB UI自动化测试中,元素定位的八大定位方式详解
  • 事务与异步方法(@Async)协同工作
  • python中print函数的flush如何使用
  • 什么是状态管理?有何种方式可以实现?它们之间有什么区别?
  • ChatGPT-4
  • MyBatis操纵数据库-XML实现(补充)
  • C语言指针与数组深度解析
  • 昆明破获一起算命破灾诈骗案,民警:大师算不到自己的未来
  • 淮安四韵·名城新章: 网络名人领略“运河之都”魅力
  • 全球前瞻|王毅赴巴西出席金砖外长会,加拿大迎来“几十年来最重要大选”
  • 我国首个核电工业操作系统发布,将在华龙一号新机组全面应用
  • 来论|如何看待韩企在美申请“饺子”专利
  • 时代邻里:拟收购成都合达联行科技剩余20%股权