算法练习第19天|222.完全二叉树的节点个数

news2024/12/23 15:06:49

222.完全二叉树的节点个数

222. 完全二叉树的节点个数 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/count-complete-tree-nodes/description/

题目描述:

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。题目数据保证输入的树是 完全二叉树

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

示例 1:

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

示例 2:

输入:root = []
输出:0

示例 3:

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

题目分析:

按照前文所讲的二叉树的递归遍历以及层序遍历,可以将完全二叉树看作普通二叉树处理,进行节点个数的统计。思路较为简单,代码如下:

后序递归遍历解法:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    //后序递归,第一步,确定函数参数以及返回值
    int countNodes(TreeNode* root) {      
        //递归第二步,确定递归终止条件。当递归函数指针root为空,递归终止
        if(root == nullptr) return 0;
        //递归第三步,确定单层递归逻辑:统计左右子树的节点个数,然后相加再加1
        int left = countNodes(root->left);  //左
        int right = countNodes(root->right);  //右
        int result = 1 + left + right;   //1就是后序遍历顺序的中           
        return result;
    }
};

层序遍历解法(也叫迭代法):

class Solution {
public:
    //层序遍历
    int countNodes(TreeNode* root) {      
        if(root == nullptr) return 0;
        queue<TreeNode*> que;
        que.push(root);
        int result = 0;
        while(!que.empty()) 
        {
            int size = que.size();
            for(int i = 0; i < size; i++)
            {
                TreeNode * node = que.front();
                que.pop();
                result++;
                if(node->left)  que.push(node->left);
                if(node->right)  que.push(node->right);
            }
        }
        return result;
    }
};

 完全二叉树解法:

但是上述方法都是将完全二叉树当做普通二叉树来进行题目的求解,这样就忽略了完全二叉树的性质。完全二叉树时除了最底层的节点可能不满之外,其余层均是满的,并且,最底层的节点必须是严格从左往右依次排列的,不能有跳跃(如下面的最右侧的二叉树,蓝色框内存在跳跃,所以其不是完全二叉树)。

如果完全二叉树最底层也填满了的话,这时它就也是满二叉树。节点个数为2^{n} -1 ,n为二叉树的层数。

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

对于情况一,可以直接用2^{n} -1来计算。

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

关键点来了,怎么判断左右孩子是不是满二叉树?在完全二叉树中,如果递归一直向左遍历的深度等于递归一直向右遍历的深度,那说明就是满二叉树。前提是一定要在完全二叉树中。

所以,该解法的递归终止条件如下所示:

if (root == nullptr) return 0; 
// 开始根据左深度和右深度是否相同来判断该子树是不是满二叉树
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
while (left) {  // 求左子树深度
    left = left->left;
    leftDepth++;
}
while (right) { // 求右子树深度
    right = right->right;
    rightDepth++;
}
if (leftDepth == rightDepth) {
    return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,返回满足满二叉树的子树节点数量
}

递归三部曲,第三部,单层递归的逻辑:(可以看出使用后序遍历)

int leftTreeNum = countNodes(root->left);       // 左
int rightTreeNum = countNodes(root->right);     // 右
int result = leftTreeNum + rightTreeNum + 1;    // 中
return result;

 整体代码如下:

class Solution {
public:
    //递归第一步
    int countNodes(TreeNode* root) { 
        //递归第二步,确定递归终止条件     
        if(root == nullptr) return 0;
        TreeNode *left = root->left;
        TreeNode *right = root->right;
        int leftDepth=0 , rightDepth = 0;
        while(left)  //一直向左遍历,求深度
        {
            leftDepth++;
            left = left->left;
        }
        while(right) //一直向右遍历,求深度
        {
            rightDepth++;
            right = right->right;
        }

        if(leftDepth == rightDepth)  //当前的完全二叉树为满二叉树
            return (2 << leftDepth) - 1;  //2的n次方减一
        
        //递归第三步,确认单层递归逻辑
        //不然的话,就递归统计左右子树的节点数。每一级递归都会进行上面的满二叉树判断
        int left = countNodes(root->left);  //左
        int right = countNodes(root->right);  //右
        int result = left + right + 1;  //中
        return result;
    }
};

该解法与后续递归解法的不同之处就在于递归终止条件,该解法核心思想致力于逐子树判断是否为满二叉树。如果是,使用2^{n} -1计算节点数;如果不是,则进行下一层的递归countNodes。思路很巧妙。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1601700.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot版本配置问题与端口占用

前言 ​ 今天在配置springboot项目时遇到了一些问题&#xff0c;jdk版本与springboot版本不一致&#xff0c;在使用idea的脚手架创建项目时&#xff0c;idea的下载地址是spring的官方网站&#xff0c;这导致所下载的版本都是比较高的&#xff0c;而我们使用最多的jdk版本是jdk…

淘宝API接口开发系列:采集商品视频,属性,sku价格,详情图等

淘宝API接口开发是一个复杂的过程&#xff0c;涉及到与淘宝开放平台的对接&#xff0c;以及理解和使用其提供的API。如果你想采集商品视频、属性、SKU价格、详情图等信息&#xff0c;你需要遵循淘宝开放平台的开发者文档&#xff0c;并确保你的应用已经获得了适当的权限。 1.请…

关于C#程序(Windows窗体应用)的退出询问

在一般的软件中我们常常会发现当我们退出系统的时候&#xff0c;总会有提示 那我们来看看这个是怎么实现的&#xff1a; 首先单击退出按钮&#xff0c;进入到我们的退出按钮属性&#xff0c;点击闪电标志&#xff1a; 找到FormClosing&#xff0c;双击进入 进行代码写入&…

Learn SRP 02

3.Editor Rendering 3.1Drawing Legacy Shaders 因为我们的管线只支持无光照的着色过程&#xff0c;使用其他不同的着色过程的对象是不能被渲染的&#xff0c;他们被标记为不可见。尽管这是正确的&#xff0c;但是它还是隐藏了场景中一些使用错误着色器的对象。所以让我们来渲…

【Golang】并发编程之三大问题:原子性、有序性、可见性

目录 一、前言二、概念理解2.1 有序性2.2 原子性后果1&#xff1a;其它线程会读到中间态结果&#xff1a;后果2&#xff1a;修改结果被覆盖 2.3 可见性1&#xff09;store buffer(FIFO)引起的类似store-load乱序现象2&#xff09;store buffer(非FIFO)引起的类似store-store乱序…

代理模式(结构型模式)

目录 1、概述 2、结构 2.1、角色分类 2.2、类图 3、静态代理 3.1、案例类图 3.2、案例代码 4、JDK 动态代理 4.1、案例代码 4.2、底层原理 4.3、执行流程说明 5、CGLib 动态代理 5.1、案例代码 6、三种代理的对比 6.1、JDK代理和CGLib代理 6.2、动态代理和静态…

【Latex排版小记录】latex设置两端对齐

Latex排版的时候遇到了公式/英文过长超出来的情况 解决办法&#xff1a;在\begin{document}里面增加\begin{sloppypar} \begin{document} \begin{sloppypar}\end{sloppypar} \end{document}

Spring Boot - 利用MDC(Mapped Diagnostic Context)实现轻量级同步/异步日志追踪

文章目录 Pre什么是MDC&#xff08;Mapped Diagnostic Context&#xff09;Slf4j 和 MDC基础工程工程结构POMlogback-spring.xmlapplication.yml同步方式方式一&#xff1a; 拦截器自定义日志拦截器添加拦截器 方式二&#xff1a; 自定义注解 AOP自定义注解 TraceLog切面 测试…

Java实现优先级队列(堆)

前言 在学习完二叉树的相关知识后&#xff0c;我们对数据结构有了更多的认识&#xff0c;本文将介绍到优先级队列(堆&#xff09; 1.优先级队列 1.1概念 前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能…

【机器学习】探究Q-Learning通过学习最优策略来解决AI序列决策问题

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

学会 Python 后可以做什么副业?

近年来&#xff0c;Python凭借其简洁易入门的特点受到越来越多人群的青睐。 当然这不仅仅是针对程序员来说&#xff0c;对于一些学生、职场人士也是如此。 Python为什么会大受欢迎呢&#xff1f;因为Python还被大家称为“胶水语言&#xff0c;它适用于网站、桌面应用开发、[自…

蓝牙耳机哪个牌子好用?力荐五款实力超群机型,快收藏!

​随着蓝牙耳机的普及&#xff0c;越来越多的年轻人开始追求这种无线的便利。市场上品牌众多&#xff0c;款式多样&#xff0c;选择起来确实让人眼花缭乱。我整理了一份蓝牙耳机品牌排行榜前十名&#xff0c;希望能为你提供一些参考&#xff0c;帮助你找到心仪的耳机。 一、如何…

C语言堆区内存管理

一、C语言编译的内存分配 二、堆区空间的分配 1、malloc函数 功能&#xff1a;从堆区分配内存 #include <stdlib.h> void *malloc(unsigned int size)//size 分配内存的字节数2、free函数 功能&#xff1a;释放内存 #include <stdlib.h> void free(void *ptr)…

L2-045 堆宝塔 - java

L2-045 堆宝塔 Java (javac) 时间限制 500 ms 内存限制 256 MB 其他编译器 时间限制 400 ms 内存限制 64 MB 栈限制 8192 KB 题目描述&#xff1a; 堆宝塔游戏是让小朋友根据抓到的彩虹圈的直径大小&#xff0c;按照从大到小的顺序堆起宝塔。但彩虹圈不一定是按照直径的大小顺…

Java NIO,高效操作I/O流的必备技能

Java IO在工作中其实不常用到&#xff0c;更别提NIO了。但NIO却是高效操作I/O流的必备技能&#xff0c;如顶级开源项目Kafka、Netty、RocketMQ等都采用了NIO技术&#xff0c;NIO也是大多数面试官必考的体系知识。虽然骨头有点难啃&#xff0c;但还是要慢慢消耗知识、学以致用哈…

Spring核心容器总结

2.2 核心容器总结 2.2.1 容器相关 BeanFactory是IoC容器的顶层接口&#xff0c;初始化BeanFactory对象时&#xff0c;加载的bean延迟加载 ApplicationContext接口是Spring容器的核心接口&#xff0c;初始化时bean立即加载 ApplicationContext接口提供基础的bean操作相关方法…

CTFHUB-技能树-Web前置技能-文件上传(无验证,JS前端验证,前端验证)

CTFHUB-技能树-Web前置技能-文件上传&#xff08;无验证&#xff0c;JS前端验证&#xff0c;前端验证—.htaccess&#xff09; 文章目录 CTFHUB-技能树-Web前置技能-文件上传&#xff08;无验证&#xff0c;JS前端验证&#xff0c;前端验证—.htaccess&#xff09;文件上传无验…

基于 LSTM 模型的古诗词自动生成算法实现及系统实现

近年来&#xff0c;研究者在利用循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;进行古诗自动生成方面取得了显著的效果。但 RNN 存在梯度问题&#xff0c;导致处理时间跨度较长的序列时 RNN 并不具备长期记忆存储功能。随后&#xff0c;出现的基…

(十一)C++自制植物大战僵尸游戏客户端更新实现

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/cFP3z 更新检查 游戏启动后会下载服务器中的版本号然后与本地版本号进行对比&#xff0c;如果本地版本号小于服务器版本号就会弹出更新提示。让用户选择是否更新客户端。 在弹出的更新对话框中有显示最新版本更新的内容…

全球化背景下的海外社媒营销战略:趋势洞察与策略调整

随着全球化的不断深入&#xff0c;企业在海外市场的竞争愈发激烈。在这一背景下&#xff0c;海外社交媒体平台成为了企业品牌推广和营销的重要渠道。本文Nox聚星将和大家探讨全球化背景下&#xff0c;企业如何利用海外社交媒体平台进行品牌推广和营销&#xff0c;并分析企业如何…