【经典算法】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

相关文章

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Before和BeforeClass的区别及说明

《Before和BeforeClass的区别及说明》:本文主要介绍Before和BeforeClass的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Before和BeforeClass的区别一个简单的例子当运行这个测试类时总结Before和Befor

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以