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

news2024/9/30 3:22:00

难度参考

        难度:中等

        分类:二叉树

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

题目

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

        输入: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.coloradmin.cn/o/1428794.html

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

相关文章

基于WordPress开发微信小程序2:决定开发一个wordpress主题

上一篇&#xff1a;基于WordPress开发微信小程序1&#xff1a;搭建Wordpress-CSDN博客 很快发现一个问题&#xff0c;如果使用别人的主题模板&#xff0c;多多少少存在麻烦&#xff0c;所以一咬牙&#xff0c;决定自己开发一个主题模板&#xff0c;并且开源在gitee上&#xff…

Javascript | JS如何断点测试(WebStorm)

JavaScript的断点与之前所学到的Java和python在jetbrain系列编辑器中的断点debug不太一样&#xff0c;往常我们在编写python的时候用pycharm的时候是直接断点进入debug的&#xff0c;就像下面这样 只要直接在代码中断点&#xff0c;然后运行debug功能即可 但是在WebStorm中不是…

网络流数据集处理(深度学习数据处理基础)

一、数据集处理 处理数据集是一个文件夹 一个文件夹处理的&#xff0c;将原网络流数据集 放入一个文件夹 处理转换成 Json文件。&#xff08;数据预处理&#xff09;然后将这些文件处理成目标文件格式 再分割成训练集和测试集。每次运行只会处理一个文件夹。 运行train.py 导入…

2024牛客寒假训练营1总结

G题不开long long的后果&#xff0c;即使有思路也没用。(给我气的) E题&#xff0c;不看数据范围的后果&#xff0c;不能一题名取题啊。 using ll long long; void solve() {int n, m;std::cin >> n >> m;std::vector<int>a(n);for (int i 0; i < n; i)…

数据分析基础之《pandas(4)—pandas画图》

1、DataFrame.plot(xNone, yNone, kindline) 说明&#xff1a; x&#xff1a;设置x轴标签 y&#xff1a;设置y轴标签 kind&#xff1a; line 折线图 bar 柱状图 hist 直方图 pie 饼图 scatter 散点图 # 找到p_change和turnover之间的关系 data.plot(xvolume, yturnover, kinds…

十一、计算机分类

1、按照性能和用途分类 计算机分类 计算机按照性能、用途和规模可以分为以下几种类型&#xff1a; 1&#xff09;巨型机&#xff08;超级计算机&#xff09; 采用大规模并行处理体系结构。运算速度最快、体积最大、价格最昂贵。主要用于尖端科学研究领域&#xff0c;如灾难预测…

【AudioPolicy To AudioHAL笔记(三)】安卓S上audio_policy_configuration.xml 加载过程分析

安卓S上audio_policy_configuration.xml 加载过程分析 /*****************************************************************************************************************/ 声明: 本博客内容均由https://blog.csdn.net/weixin_47702410原创&#xff0c;转载or引用请注明…

Iceberg从入门到精通系列之二十三:Spark查询

Iceberg从入门到精通系列之二十三&#xff1a;Spark查询 一、使用 SQL 查询二、使用 DataFrame 进行查询三、Time travel四.Incremental read五、检查表六、History七、元数据日志条目八、Snapshots九、Files十、Manifests十一、Partitions十二、所有元数据表十三、参考十四、使…

flv视频格式批量截取封面图(不占内存版)--其他视频格式也通用

flv视频格式批量截取封面图&#xff08;不占内存版&#xff09;--其他视频格式也通用 需求&#xff08;实现的效果&#xff09;功能实现htmlcssjs 需求&#xff08;实现的效果&#xff09; 批量显示视频&#xff0c;后端若返回有imgUrl,则直接显示图1&#xff0c; 若无&#xf…

安卓SurfaceTexture中updateTexImage使用及源码分析

文章目录 引言updateTexImage 简单使用SurfaceTexture 初始化相关源码分析Surface 绘制流程源码分析createBufferQueue 源码分析SurfaceTexture 之 updateTexImage 源码分析结尾 本文首发地址 https://h89.cn/archives/140.html 最新更新地址 https://gitee.com/chenjim/chenji…

2024数模美赛C题F题完整代码结果展示

C&#xff1a;Momentum in Tennis 实在精力有限&#xff0c;完整讲解可以移步去看我的讲解视频啦&#xff1a; 美赛C题每一问代码结果讲解及进度说明 F&#xff1a;Reducing lllegal Wildlife Trade 实在精力有限&#xff0c;完整讲解可以移步去看我的讲解视频啦&#xff1a; ​…

近期CCF系列会议截稿时间

专属领域会议订阅 关注{晓理紫}&#xff0c;每日更新会议信息&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新会议信息。 CSFW (CCF B) IEEE Computer Security Foun…

【gcc】webrtc发送侧计算 丢包率

大神的分析 : 提到: 每当收到cc-feedback或者收到RR-report的时候就能统计出丢包率,在cc-controller中就会调用SendSideBandwidthEstimation::UpdatePacketsLost()去更新丢包率,同时进行码率预估 G:\CDN\rtcCli\m98\src\modules\congestion_controller\goog_cc\send_side_b…

假期无忧!这个微信自动回复工具助您高效管理与客户互动

在繁忙的现代生活中&#xff0c;尤其是在假期期间&#xff0c;处理大量微信消息可能会成为一项艰巨的任务。今天就给大家分享一个能够让微信自动回复消息的工具&#xff0c;帮助你解放双手&#xff0c;让你的假期无忧&#xff01; 通过微信管理系统&#xff0c;您可以同时登录…

152基于matlab的GUI滚动轴承特征频率计算

基于matlab的GUI滚动轴承特征频率计算&#xff0c;输入轴承参数&#xff0c;包括转速&#xff0c;节圆直径、滚子直径、滚子数、接触角&#xff0c;就可得滚动特征频率结果&#xff0c;程序已调通&#xff0c;可直接运行。 152 matlab 轴承特征频率 GUI (xiaohongshu.com)

uni-app 经验分享,从入门到离职(三)——关于 uni-app 生命周期快速了解上手

文章目录 &#x1f4cb;前言⏬关于专栏 &#x1f3af;什么是生命周期&#x1f9e9;应用生命周期&#x1f4cc; 关于 App.vue/App.uvue &#x1f9e9;页面生命周期&#x1f4cc;关于 onShow 与 onLoad 的区别 &#x1f9e9;组件生命周期 &#x1f4dd;最后 &#x1f4cb;前言 这…

C# SSH.NET 长命令及时返回

在SSH中执行长时间的命令&#xff0c;SSH.NET及时在文本框中返回连续显示结果。 c# - Execute long time command in SSH.NET and display the results continuously in TextBox - Stack Overflow 博主管理了一个服务器集群&#xff0c;准备上自动巡检工具&#xff0c;测试在…

clickhouse如何清除多个分区数据 alter table drop partition操作

官网drop partition操作 官网链接&#xff1a;https://clickhouse.com/docs/zh/sql-reference/statements/alter/partition#drop-partitionpart 官网上之有清除单个分区的例子&#xff0c;并没有对清除多个分区的场景进行描述&#xff0c;之前清除分区时也是按照官网例子进行…

python爬虫4

#1.练习 # &#xff08;1&#xff09; 获取网页的源码 # &#xff08;2&#xff09; 解析 解析的服务器响应的文件 etree.HTML # (3) 打印 import urllib.request urlhttps://www.baidu.com/ headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit…

2024年美赛B题潜水器定位和搜救建模代码和完整论文文档

目前已完成2024年美赛B题潜水器定位和搜救的建模代码和论文编写&#xff0c;部分文章内容和代码如下&#xff1a; 摘要 在海洋探险和搜救领域&#xff0c;潜水器的定位和搜救任务具有重要意义。本文旨在开发一系列模型来预测潜水器位置、分析不确定性、确定信息传递策略、建议…