【经典算法】LeetCode 222. 完全二叉树的节点个数(Java/C/Python3实现含注释说明,Easy)

本文主要是介绍【经典算法】LeetCode 222. 完全二叉树的节点个数(Java/C/Python3实现含注释说明,Easy),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 作者主页: 🔗进朱者赤的博客

  • 精选专栏:🔗经典算法

  • 作者简介:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名

  • ❤️觉得文章还不错的话欢迎大家点赞👍➕收藏⭐️➕评论,💬支持博主,记得点个大大的关注,持续更新🤞
    ————————————————-

题目描述

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

原题:LeetCode 222

思路及实现

方式一:递归遍历

思路

递归遍历整棵树,每个节点都返回其子树的大小,最终相加即为整个树的大小。

代码实现

Java版本
// 假设树的节点定义如下
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class Solution {public int countNodes(TreeNode root) {if (root == null) return 0;return 1 + countNodes(root.left) + countNodes(root.right);}
}

说明:这是最基本的递归方法,简单易懂但效率不高,因为会遍历整个树。

C语言版本
// 假设树的节点定义如下
struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;
};int countNodes(struct TreeNode* root) {if (root == NULL) return 0;return 1 + countNodes(root->left) + countNodes(root->right);
}

说明:与Java版本类似,只是语法不同。

Python3版本
# 假设树的节点定义如下
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightclass Solution:def countNodes(self, root: TreeNode) -> int:if not root: return 0return 1 + self.countNodes(root.left) + self.countNodes(root.right)

说明:Python版本的实现与Java和C类似。

Golang版本
// 假设树的节点定义如下
type TreeNode struct {Val   intLeft  *TreeNodeRight *TreeNode
}func countNodes(root *TreeNode) int {if root == nil {return 0}return 1 + countNodes(root.Left) + countNodes(root.Right)
}

说明:Golang版本的实现与上述语言类似。

复杂度分析

  • 时间复杂度:O(N),其中N为树的节点个数。每个节点都遍历了一次。
  • 空间复杂度:O(H),其中H为树的高度。递归调用栈的深度最大为树的高度。

方式二:二分查找+递归

思路

利用完全二叉树的性质,先找到树的高度,然后利用二分查找确定左子树或右子树中最后一层满二叉树的节点个数,递归计算剩余部分。

以下是按照给定思路实现的完全二叉树节点数统计的Java、C、Python3和Go语言的代码,以及相应的复杂度分析。

代码实现

Java
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class Solution {public int countNodes(TreeNode root) {if (root == null) return 0;int leftHeight = getHeight(root.left);int rightHeight = getHeight(root.right);if (leftHeight == rightHeight) {// 左子树是满二叉树return (1 << leftHeight) + countNodes(root.right);} else {// 左子树不是满二叉树,递归计算左子树return 1 + countNodes(root.left);}}private int getHeight(TreeNode node) {if (node == null) return 0;return 1 + getHeight(node.left);}
}
C
#include <stdio.h>
#include <stdlib.h>typedef struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;
} TreeNode;int getHeight(TreeNode* node) {if (node == NULL) return 0;return 1 + getHeight(node->left);
}int countNodes(TreeNode* root) {if (root == NULL) return 0;int leftHeight = getHeight(root->left);int rightHeight = getHeight(root->right);if (leftHeight == rightHeight) {return (1 << leftHeight) + countNodes(root->right);} else {return 1 + countNodes(root->left);}
}
Python3
class TreeNode:def __init__(self, x):self.val = xself.left = Noneself.right = Nonedef getHeight(node):if node is None:return 0return 1 + getHeight(node.left)def countNodes(root):if root is None:return 0leftHeight = getHeight(root.left)rightHeight = getHeight(root.right)if leftHeight == rightHeight:return (1 << leftHeight) + countNodes(root.right)else:return 1 + countNodes(root.left)
Go
package mainimport ("fmt""math"
)type TreeNode struct {Val   intLeft  *TreeNodeRight *TreeNode
}func getHeight(node *TreeNode) int {if node == nil {return 0}return 1 + getHeight(node.Left)
}func countNodes(root *TreeNode) int {if root == nil {return 0}leftHeight := getHeight(root.Left)rightHeight := getHeight(root.Right)if leftHeight == rightHeight {return int(math.Pow(2, float64(leftHeight))) + countNodes(root.Right)} else {return 1 + countNodes(root.Left)}
}func main() {// Test code// ...
}

复杂度分析

  • 时间复杂度:O(N log N),其中 N 是树的节点数。在最坏情况下,当树接近满二叉树时,每次递归调用 getHeight 都会遍历树的一部分,直到找到完全二叉树的边界。由于二分查找的性质,递归调用的次数为 O(log N),但每次递归可能需要遍历树的大部分节点,因此总的时间复杂度为 O(N log N)。然而,在平均情况下,由于使用了二分查找,性能会优于最坏情况。

  • 空间复杂度:O(H),其中 H 是树的高度。这是由递归调用栈的深度决定的。在完全二叉树中,H 通常远小于 N(节点数),但在最坏情况下(树接近满二叉树),H 接近于 log N。因此,空间复杂度在最坏情况下为 O(log N)。

总结

以下是针对完全二叉树节点数统计的两种方式的总结:

方式优点缺点时间复杂度空间复杂度
方式一(层次遍历)直观易懂,不依赖特殊性质代码量较大,需要额外的空间存储队列O(N)O(N)(队列空间)
方式二(二分查找+递归)利用完全二叉树的性质,平均性能较好递归调用栈可能较深,最坏情况下时间复杂度较高O(N log N)O(H)(H为树的高度,通常小于N)

相似题目

以下是与完全二叉树节点数统计相似的题目,这些题目可能需要类似的算法思想或者技巧来解决:

相似题目难度链接
105. 从前序与中序遍历序列构造二叉树中等LeetCode 105
106. 从中序与后序遍历序列构造二叉树中等LeetCode 106
110. 平衡二叉树简单LeetCode 110
111. 二叉树的最小深度简单LeetCode 111
112. 路径总和简单LeetCode 112
222. 完全二叉树的节点个数中等LeetCode 222(本题)
543. 二叉树的直径简单LeetCode 543
572. 另一个树的子树中等LeetCode 572
993. 二叉树的堂兄弟节点中等LeetCode 993
104. 二叉树的最大深度简单LeetCode 104

请注意,这些题目可能并不完全与完全二叉树节点数统计具有相同的解题技巧,但它们都涉及到了二叉树的遍历、性质利用、递归、DFS/BFS等常见的算法思想。

欢迎一键三连(关注+点赞+收藏),技术的路上一起加油!!!代码改变世界

  • 关于我:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名),回复暗号,更能获取学习秘籍和书籍等

  • —⬇️欢迎关注下面的公众号:进朱者赤,认识不一样的技术人。⬇️—

这篇关于【经典算法】LeetCode 222. 完全二叉树的节点个数(Java/C/Python3实现含注释说明,Easy)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1046525

相关文章

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Python实现特殊字符判断并去掉非字母和数字的特殊字符

《Python实现特殊字符判断并去掉非字母和数字的特殊字符》在Python中,可以通过多种方法来判断字符串中是否包含非字母、数字的特殊字符,并将这些特殊字符去掉,本文为大家整理了一些常用的,希望对大家... 目录1. 使用正则表达式判断字符串中是否包含特殊字符去掉字符串中的特殊字符2. 使用 str.isa

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Spring 基于XML配置 bean管理 Bean-IOC的方法

《Spring基于XML配置bean管理Bean-IOC的方法》:本文主要介绍Spring基于XML配置bean管理Bean-IOC的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一... 目录一. spring学习的核心内容二. 基于 XML 配置 bean1. 通过类型来获取 bean2. 通过

Spring Boot 集成 Quartz并使用Cron 表达式实现定时任务

《SpringBoot集成Quartz并使用Cron表达式实现定时任务》本篇文章介绍了如何在SpringBoot中集成Quartz进行定时任务调度,并通过Cron表达式控制任务... 目录前言1. 添加 Quartz 依赖2. 创建 Quartz 任务3. 配置 Quartz 任务调度4. 启动 Sprin

springboot上传zip包并解压至服务器nginx目录方式

《springboot上传zip包并解压至服务器nginx目录方式》:本文主要介绍springboot上传zip包并解压至服务器nginx目录方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录springboot上传zip包并解压至服务器nginx目录1.首先需要引入zip相关jar包2.然

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my