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

相关文章

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Java.lang.InterruptedException被中止异常的原因及解决方案

《Java.lang.InterruptedException被中止异常的原因及解决方案》Java.lang.InterruptedException是线程被中断时抛出的异常,用于协作停止执行,常见于... 目录报错问题报错原因解决方法Java.lang.InterruptedException 是 Jav

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

Java Stream流之GroupBy的用法及应用场景

《JavaStream流之GroupBy的用法及应用场景》本教程将详细介绍如何在Java中使用Stream流的groupby方法,包括基本用法和一些常见的实际应用场景,感兴趣的朋友一起看看吧... 目录Java Stream流之GroupBy的用法1. 前言2. 基础概念什么是 GroupBy?Stream

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统