算法练习-二叉树的节点个数【完全/普通二叉树】(思路+流程图+代码)

难度参考

        难度:中等

        分类:二叉树

        难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记,旨在督促自己认真学习。

题目

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

        输入: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
        题目数据保证输入的树是完全二叉树

思路

        对于这道题目要求计算一个完全二叉树的节点个数,可以利用完全二叉树的性质来解决,而不需要对整个树进行遍历。

        完全二叉树是指除了最后一层外,每一层的节点都被填满,并且最后一层的节点靠左排列的二叉树。简单来说,完全二叉树是一种具有最优结构的二叉树。

        下面是一个完全二叉树的示例:

      A/   \B     C/ \   /D   E F

        在这个示例中,树的每一层都被节点填满,最后一层的节点靠左排列。

        需要注意的是,对于完全二叉树,叶节点只会出现在最后两层,并且最后一层的叶节点都靠左排列。

        完全二叉树的性质包括

  1. 对于树的任意节点,如果它的深度(从根节点到达该节点的路径的长度)为 d,则以该节点为根的子树一定是一个高度为 h-d+1 的满二叉树。

    • 这是因为完全二叉树的定义要求除了最后一层外,其他层都是满的,而最后一层的节点从左到右连续排列。
  2. 一棵高度为 h 的满二叉树,其节点个数为 2^h-1。

        完全二叉树与满二叉树有一定的关系,但并不是完全一样的概念。

        满二叉树是一种特殊的二叉树它的每一层都被节点填满,所有的叶节点都在同一层上。换句话说,满二叉树的节点数达到了最大值。满二叉树的层数是固定的。

        而完全二叉树除了最后一层外,每一层的节点都被填满,并且最后一层的节点靠左排列。完全二叉树的节点数可以少于满二叉树,最后一层不一定是满的

        所以可以说,满二叉树是完全二叉树的一种特殊情况,但完全二叉树不一定是满二叉树。完全二叉树的层数可以少于满二叉树的层数,节点数也可以少于满二叉树的节点数。

        下面是一个满二叉树的示例:

      A/   \B     C/ \   / \D   E F   G

        基于上述性质,我们可以采用以下步骤来计算完全二叉树的节点个数:

  1. 首先,计算树的左子树和右子树的深度 leftDepth 和 rightDepth。

  2. 如果 leftDepth 等于 rightDepth,则说明左子树是一个满二叉树,且其节点个数可以通过公式 2^leftDepth-1 计算得到。

    • 在这种情况下,我们只需要递归计算右子树的节点个数,然后加上左子树的节点个数和根节点,即可得到完整的节点个数。
  3. 如果 leftDepth 不等于 rightDepth,则说明右子树是一个满二叉树,且其节点个数可以通过公式 2^rightDepth-1 计算得到。

    • 在这种情况下,我们只需要递归计算左子树的节点个数,然后加上右子树的节点个数和根节点,即可得到完整的节点个数。

        通过实现上述步骤,我们可以快速地计算出完全二叉树的节点个数。

示例

        假设我们有以下二叉树:

     1/   \2     3/ \   /
4   5 6

        首先,根节点存在,我们开始计算左子树的高度。从根节点1开始,我们通过左子节点2,再通过左子节点4,没有更多的左子节点,此时左子树的高度为3。

        接下来,我们计算右子树的高度。从根节点1开始,我们通过右子节点3,再通过左子节点6,没有更多的右子节点,此时右子树的高度为2。

        由于左子树的高度(3)不等于右子树的高度(2),所以我们知道右子树是完全二叉树。现在,我们递归地计算右子树的节点个数。右子树只有一个节点6,所以它的节点个数为1。

        由于左子树的高度(3)大于右子树的高度(2),所以我们知道左子树不是完全二叉树。我们需要递归地计算左子树的节点个数。左子树有两个子节点4和5,因此左子树的节点个数为2。

        最后,我们将左子树的节点个数(2)、右子树的节点个数(1)和根节点(1)相加,得到整个二叉树的节点个数为6。

梳理

        我们通过判断左子树的高度和右子树的高度来确定给定二叉树是否是完全二叉树。

        判断一个二叉树是否是完全二叉树的方法如下:

  1. 首先,我们计算左子树和右子树的高度,使用两个循环分别计算它们的高度。
  2. 如果左子树的高度等于右子树的高度,说明左子树是一个满二叉树(即每个节点都有两个子节点),而右子树可能是一个完全二叉树。在完全二叉树中,所有的节点都尽量靠左排列,且最后一层节点从左到右依次填充。因此,在满二叉树的情况下,我们可以使用一个简单的公式计算出左子树的节点个数:(1 << leftHeight) - 1。其中,<< 是位移运算符,表示将1左移leftHeight位,即乘以2的leftHeight次方,然后减去1。
  3. 如果左子树的高度不等于右子树的高度,说明左子树不是一个满二叉树,而右子树可能是一个完全二叉树。在这种情况下,我们递归地计算左子树和右子树的节点个数,并将结果相加再加上根节点的1。

        通过这种判断逻辑,我们可以正确地计算给定二叉树的节点个数。

代码

#include<iostream>
#include<vector>
using namespace std;// 二叉树的节点定义
struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};// 计算完全二叉树的节点个数
int countNodes(TreeNode* root) {if (root == NULL) {return 0;}int leftHeight = 0;int rightHeight = 0;TreeNode* leftNode = root;TreeNode* rightNode = root;// 计算左子树的高度while (leftNode != NULL) {leftNode = leftNode->left;leftHeight++;}// 计算右子树的高度while (rightNode != NULL) {rightNode = rightNode->right;rightHeight++;}// 如果左子树的高度等于右子树的高度,则说明左子树是完全二叉树if (leftHeight == rightHeight) {// 计算左子树的节点个数:2^leftHeight-1,加上根节点1,即为总节点个数return (1 << leftHeight) - 1;} else {// 如果左子树的高度不等于右子树的高度,则说明右子树是完全二叉树// 计算右子树的节点个数:2^rightHeight-1,加上根节点1,即为总节点个数return countNodes(root->left) + countNodes(root->right) + 1;}
}int main() {// 构建示例树 [1,2,3,4,5,6]TreeNode* root = new TreeNode(1);root->left = new TreeNode(2);root->right = new TreeNode(3);root->left->left = new TreeNode(4);root->left->right = new TreeNode(5);root->right->left = new TreeNode(6);// 计算节点个数int count = countNodes(root);// 输出结果cout << "节点个数: " << count << endl;return 0;
}

进阶

        如果是一棵普通二叉树?

思路

        当求任意一棵二叉树的节点个数时,可以使用递归的方式来解决。下面是一种求二叉树节点个数的思路和题解:

  1. 如果根节点为空,则树中没有节点,返回节点个数为0。
  2. 否则,递归计算根节点的左子树的节点个数,记为 leftCount
  3. 递归计算根节点的右子树的节点个数,记为 rightCount
  4. 节点个数等于左子树节点个数、右子树节点个数和根节点本身的总和(即 leftCount + rightCount + 1)。
  5. 返回节点个数作为结果。

示例

        直接用了之前的输入(普通也包含完全)。

  1. 要计算整个树的节点个数,开始于根节点1。
  2. 根节点1本身是一个节点,因此开始时计数为1。
  3. 接下来,递归地计算节点2的子树节点个数。节点2有两个孩子,节点4和节点5,它们都没有孩子,所以子树2的节点个数是1(节点2本身)+1(节点4)+1(节点5)=3。
  4. 同样,节点3也被递归地计算。节点3有一个孩子,节点6,它没有孩子,所以节点3的子树节点个数是1(节点3本身)+1(节点6)=2。
  5. 将所有计数相加:1(根节点1)+3(节点2及其子树的节点数)+2(节点3及其子树的节点数)=6。

        所以最终,整棵树的节点个数为6。

        这个过程是通过代码中的递归函数countNodes实现的。函数countNodes按照上述逻辑,以树的根节点开始访问每个节点,并递归地遍历它们的左右子树,计算每个子树的节点数,并将它们与当前节点的数量(1)相加。当遇到空子树(即NULL,或没有子节点的节点)时,递归调用返回0,表示这里没有更多的节点需要计数。通过这种方式,所有节点被计数并求和,得到整棵树的节点总数。   

梳理

        普通二叉树和完全二叉树在结构上存在显著不同,因此求解它们节点个数的最佳方法往往也不同。这些不同主要表现在:

  1. 普通二叉树:它的结构没有特定的规则,节点可以任意地分布在左右子树中。为了计算普通二叉树中的节点数,我们通常采用递归方法,对每个节点进行遍历。递归过程中,当遇到一个非空节点时,就将计数加1,并继续递归计算它的左右子节点。

  2. 完全二叉树:它是一种特殊的二叉树,所有的层都是满的,除了最后一层,最后一层的节点尽量都向左排列。这种结构使得我们可以利用完全二叉树的性质来用非递归的方法来计算节点数。例如,可以通过计算树高以及最后一层节点的数量来求得总节点数。因为在完全二叉树中,除了最后一层外,其余每层的节点数都是已知的(即 2^层级数 - 1)。

        可以使用普通二叉树计算节点个数的方法来计算完全二叉树的节点数,即递归遍历每个节点,这种方法在任何类型的二叉树中都适用。然而,当树的高度较大时,这种方法相对较慢,因为它需要访问树中的每个节点。

        针对完全二叉树,利用其特殊性质(每层节点数等比递增、最后一层节点尽量左对齐等),可以设计出更为高效的算法(其他方法回头补)。

代码

#include <iostream>
using namespace std;// 定义二叉树节点结构体
struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};// 用于计算二叉树节点数的递归函数
int countNodes(TreeNode* root) {if (root == NULL) {return 0;} else {return 1 + countNodes(root->left) + countNodes(root->right);}
}// 主函数
int main() {// 创建二叉树// 注意:这里仅作为示例,具体创建二叉树的方法取决于输入格式和树结构TreeNode* root = new TreeNode(1);root->left = new TreeNode(2);root->right = new TreeNode(3);root->left->left = new TreeNode(4);root->left->right = new TreeNode(5);root->right->left = new TreeNode(6);// 计算节点数并输出结果cout << "Number of nodes: " << countNodes(root) << endl;// 清理申请的内存(此处未显示,但在实际使用中需要处理)// 如通过后序遍历删除所有节点// deleteTree(root);return 0;
}

打卡

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

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

相关文章

广义表-C语言

广义表&#xff08;Generalized List&#xff09;是一种扩展了线性表的数据结构&#xff0c;它在线性表的基础上增加了元素可以是表的特点。在广义表中&#xff0c;元素不仅可以是单个的数据元素&#xff0c;还可以是一个子表&#xff0c;而子表中的元素也可以是数据元素或其他…

Java on VS Code 2024年1月更新|JDK 21支持!测试覆盖率功能最新体验!

作者&#xff1a;Nick Zhu - Senior Program Manager, Developer Division At Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到 Visual Studio Code for Java 2024年的第一期更新&#xff01;提前祝愿大家春节快乐&#xff01;在本博客中&#xff0c;我们将有…

jvm一级缓存

1、利用JVM缓存。脱离redis。 2、导包&#xff0c;springboot自带此包。如没有可以导&#xff1a;com.google.guava:guava:20.0的包。 3、直接上代码&#xff1a; package com.leo.cache;import com.alibaba.fastjson.JSONObject; import com.google.common.cache.Cache; im…

开源软件:引领技术创新、商业模式与安全的融合

序 在信息技术日新月异的今天&#xff0c;开源软件以其独特的魅力和影响力&#xff0c;正逐渐成为软件产业的新常态。开源软件的低成本、高度可协作性和透明度等特点&#xff0c;不仅吸引了无数企业和个人用户的青睐&#xff0c;更为软件行业带来了前所未有的繁荣景象。 一、…

复旦微 zynq amp cpu0 唤醒启动cpu1

1 配置多核amp工程&#xff0c;参考上一篇文章 https://blog.csdn.net/yangchenglin927/article/details/136057534 2 在cpu0的main函数中增加唤醒代码 active_cpu1(); /** helloworld.c: simple test application** This application configures UART 16550 to baud rate 96…

Flink SQL Client 安装各类 Connector、Format 组件的方法汇总(持续更新中....)

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

DAY43:背包问题提升1049、494、474

Leetcode: 1049 最后一块石头的重量 II 这道题和昨天的最后一道题很像&#xff0c;都是重量和价值一样等于stone[i]。 本质思想是尽量将石头分成相似的两堆。如果出现两堆价值一样&#xff0c;那0&#xff0c;如果不一样&#xff0c;就用大的那堆减去小的那堆就是最后相撞之后…

数据结构——D/二叉树

&#x1f308;个人主页&#xff1a;慢了半拍 &#x1f525; 创作专栏&#xff1a;《史上最强算法分析》 | 《无味生》 |《史上最强C语言讲解》 | 《史上最强C练习解析》 &#x1f3c6;我的格言&#xff1a;一切只是时间问题。 ​ 1.树概念及结构 1.1树的概念 树是一种非线性的…

【Android辟邪】之:gradle——在项目间共享依赖关系版本

翻译和简单修改自&#xff1a;https://docs.gradle.org/current/userguide/platforms.html#sec:sharing-catalogs 建议看原文&#xff08;有能力的话&#xff09; 现在 Gradle 脚本可以使用两种语法编写&#xff1a;Kotlin 和 Groovy 本文只使用kotlin脚本语法&#xff0c;更…

10.0 Zookeeper 权限控制 ACL

zookeeper 的 ACL&#xff08;Access Control List&#xff0c;访问控制表&#xff09;权限在生产环境是特别重要的&#xff0c;所以本章节特别介绍一下。 ACL 权限可以针对节点设置相关读写等权限&#xff0c;保障数据安全性。 permissions 可以指定不同的权限范围及角色。 …

人工智能:数据分析之数据预处理、分析模型与可视化

在人工智能和数据科学领域&#xff0c;数据分析是一种核心过程&#xff0c;它帮助我们从大量的数据中提取有价值的信息。数据分析的质量和结果直接影响到决策的效率和准确性。在这篇博客中&#xff0c;我们将详细探讨数据分析的关键步骤&#xff0c;包括数据预处理、分析模型和…

Oracle 面试题 | 15.精选Oracle高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

AR人脸106240点位检测解决方案

美摄科技针对企业需求推出了AR人脸106/240点位检测解决方案&#xff0c;为企业提供高效、精准的人脸识别服务&#xff0c;采用先进的人脸识别算法和机器学习技术&#xff0c;通过高精度、高速度的检测设备&#xff0c;对人脸进行快速、准确地定位和识别。该方案适用于各种应用场…

Backtrader 文档学习- Observers - Benchmarking

Backtrader 文档学习- Observers - Benchmarking 1.概述 backtrader包括两种不同类型的对象&#xff0c;可以帮助跟踪&#xff1a; Observers 观察者Analyzers 分析器 在分析器领域中&#xff0c;已有TimeReturn对象&#xff0c;用于跟踪整个组合价值&#xff08;即包括现金…

黑马Java——集合进阶(List、Set、泛型、树)

一、集合的体系结构 1、单列集合&#xff08;Collection&#xff09; 二、Collection集合 1、Collection常见方法 1.1代码实现&#xff1a; import java.util.ArrayList; import java.util.Collection;public class A01_CollectionDemo1 {public static void main(String[] a…

Token、CAS、JWT和OAuth 2.0认证系统认证中心系统设计对比与实践总结

在现代应用开发中&#xff0c;身份认证是一个关键的问题。为了解决身份认证的需求&#xff0c;开发人员可以选择不同的认证系统&#xff0c;如Token、CAS&#xff08;Central Authentication Service&#xff09;和JWT&#xff08;JSON Web Token&#xff09;OAuth 2.0认证系统…

大厂聚合支付系统架构演进(上)

点击下方“JavaEdge”&#xff0c;选择“设为星标” 第一时间关注技术干货&#xff01; 关注我&#xff0c;紧跟本系列专栏文章&#xff0c;咱们下篇再续&#xff01; 作者简介&#xff1a;魔都国企技术专家兼架构&#xff0c;多家大厂后端一线研发经验&#xff0c;各大技术社区…

1.0 Zookeeper 分布式配置服务教程

ZooKeeper 是 Apache 软件基金会的一个软件项目&#xff0c;它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。 ZooKeeper 的架构通过冗余服务实现高可用性。 Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来&#xff0c;构成一个高…

C++的缺省参数和函数重载

目录 1.缺省参数 1.1缺省参数的概念 1.2缺省参数的分类 1.3缺省参数使用场景 2.函数重载 2.1函数重载的概念 2.2构成函数重载 1.缺省参数 1.1缺省参数的概念 概念&#xff1a;缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时&#xff0c;如果没…

GPTs保姆级教程之实践

GPTs什么 使用GPTs的前提&#xff1a;ChatGPT Plus帐号 GTPs的作用&#xff1a;把我们和GPT对话的prompt&#xff0c;封装起来成为一个“黑匣子”。 主要有两个作用&#xff1a; 1、避免反复输入prompt&#xff0c;“黑匣子”打开&#xff0c;输入问题即可使用 2、在别人可以…