DAY16 二叉树最大深度最小深度完全二叉树节点个数

9.二叉树的最大深度

递归法

后序遍历

本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

而根节点的高度就是二叉树的最大深度,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。

用后序遍历(左右中)来计算树的高度:

1.确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。

int getdepth(TreeNode* node)

2.确定终止条件:如果为空节点的话,就返回0,表示高度为0。

if (node == NULL) return 0;

3.确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。

class solution {
public:
    int getdepth(TreeNode* node) {
        if (node == NULL) return 0;
        int leftdepth = getdepth(node->left);       // 左
        int rightdepth = getdepth(node->right);     // 右
        int depth = 1 + max(leftdepth, rightdepth); // 中
        return depth;
    }
    int maxDepth(TreeNode* root) {
        return getdepth(root);
    }
};
前序遍历

充分表现出求深度回溯的过程

class solution {
public:
    int result;
    void getdepth(TreeNode* node, int depth) {
        result = depth > result ? depth : result; // 中

        if (node->left == NULL && node->right == NULL) return ;

        if (node->left) { // 左
            depth++;    // 深度+1
            getdepth(node->left, depth);
            depth--;    // 回溯,深度-1   
        }
        if (node->right) { // 右
            depth++;    // 深度+1
            getdepth(node->right, depth);
            depth--;    // 回溯,深度-1
        }
        return ;
    }
    int maxDepth(TreeNode* root) {
        result = 0;
        if (root == NULL) return result;
        getdepth(root, 1);
        return result;
    }
};

简化代码

class solution {
public:
    int result;
    void getdepth(TreeNode* node, int depth) {
        result = depth > result ? depth : result; // 中
        if (node->left == NULL && node->right == NULL) return ;
        if (node->left) { // 左
            getdepth(node->left, depth + 1);
        }
        if (node->right) { // 右
            getdepth(node->right, depth + 1);
        }
        return ;
    }
    int maxDepth(TreeNode* root) {
        result = 0;
        if (root == 0) return result;
        getdepth(root, 1);
        return result;
    }
};

迭代法

使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。

在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:

层序遍历

所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。

class Solution{
public:
	 int maxDepth(TreeNode* root) {
      	if(root == NULL) return 0;
         queue<TreeNode*> que;
         que.push(root);
         int depth = 0;
         while(!que.empty()){
             int size = que.size();
             depth++;
             for(int i = 0;i < size; i++){
                TreeNode* node = que.front();
                 que.pop();
                 if(node->left) que.push(node->left);
                 if(node->right) que.push(node->right);
             }
         }
         return depth;
     }
}

总结

1.使用前序(中左右),这样是从上往下,就是深度的概念。

2.也可以使用后序遍历(左右中),这样就是从下往上,就是高度的概念。使用前序求的就是深度,使用后序求的是高度

递归法始终不是很会,层序遍历容易理解点。

10.二叉树最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

**说明:**叶子节点是指没有子节点的节点。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:2
  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始)

后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。

在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:

111.二叉树的最小深度

题目中说的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。,注意是叶子节点

什么是叶子节点,左右孩子都为空的节点才是叶子节点

递归法

后序遍历方式
  1. 确定递归函数的参数返回值

参数为要传入的二叉树根节点,返回的是int类型的深度。

int getDepth(TreeNode* node)
  1. 确定终止条件

终止条件也是遇到空节点返回0,表示当前节点的高度为0。

if (node == NULL) return 0;

​ 3.确定单层递归的逻辑

int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
int result = 1 + min(leftDepth, rightDepth);//如果这么求的话,没有左孩子的分支会算为最短深度。
return result;

如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。

反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。

最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。

int leftDepth = getDepth(node->left);//左
int rightDepth = getDepth(node->right);//右
//中,不做左右节点为空的判断的话,出现空节点时就会返回最小值为 1 + 0 = 1的情况,
if(node->left ==NULL && node->right != NULL){ // 当一个左子树为空,右不为空,这时并不是最低点
    return 1 + rightDepth;
}
if(node->left !=NULL && node->right =-= NULL){// 当一个右子树为空,左不为空,这时并不是最低点
    return 1 + leftDepth;
}
return 1 + min(leftDepth,rightDepth);//左右孩子都为空

遍历的顺序为后序(左右中),可以看出:求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。

前序遍历方式
class Solution {
public:
    int result = INT_MAX;//默认最大值
    void getDepth(TreeNode* node,int depth){
        if(node == NULL) return ;
        //中
        if(node->left == NULL && node->right == NULL){//满足叶子节点
            result = min(result, depth);//更新结果
            return;
        }
        //左
        if(node->left != NULL){//左子树
            getDepth(node->left,depth + 1);
        }
        //右
        if(node->right != NULL){//右子树
            getDepth(node->right,depth + 1);
        }
        return ;
    }
    int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        getDepth(root,1);
        return result;
    }
};

迭代法

有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:

    int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        queue<TreeNode*> que;
        que.push(root);
        int minDepth = 0;
        while(!que.empty()){
            int size = que.size();
            minDepth++;
            for(int i = 0; i < size ; i ++){
                TreeNode* node = que.front();
                que.pop();
                if(node->left == nullptr && node->right ==nullptr){
                    return minDepth;
                }
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return minDepth;
    }
};

总结

1.什么是最小深度?什么是叶子节点?

根节点到叶子节点的最短距离。左右子节点都为空的节点就是叶子节点。

2.后序遍历,左右中,就是从下往上,求的是高度,每次把结果返回给父节点, 最大高度 = 最大深度。

前序遍历,中左右,就是从上往下,求的是深度。

11.完全二叉树节点个数

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。

示例 1:

img

输入:root = [1,2,3,4,5,6]
输出:6

迭代法

层序遍历:记录遍历的节点数量就可以了

class Solution {
public:
    int countNodes(TreeNode* root) {
        if(root == NULL) return 0;
        queue<TreeNode*> que;
        que.push(root);
        int num = 1;
        while(!que.empty()){
            int size = que.size();
            for(int i = 0;i < size; i++){
                TreeNode* node = que.front();
                que.pop();
                if(node->left){
                   que.push(node->left);
                   num++; 
                } 
                if(node->right){
                  que.push(node->right); 
                  num++; 
                } 
            }
        }
        return num;
    }
};

递归法

和求二叉树的深度写法类似。

  1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。

代码如下:

int getNodesNum(TreeNode* cur) {}

2.确定终止条件:如果为空节点就返回0,表示节点数为0.

if(cur == NULL) return 0;

3.确定单层递归逻辑:先求左子树的节点数量,再求右子树的节点数量,最后取总和+1(加1是因为算上当前中间节点)就是总的节点个数。

int left = getNodesNum(cur->left);//左
int right = getNodesNum(cur->right);//右
return left+right+1;//中
class Solution {
public:
    int getNodesNum(TreeNode* cur){
        if(cur == nullptr) return 0;
        int left = getNodesNum(cur->left);//左
        int right = getNodesNum(cur->right);//右
        return 1 + left + right;//中
		//return 1 + countNodes(root->left) + countNodes(root->right);//简化代码
    }

    int countNodes(TreeNode* root) {
        if(root == nullptr) return 0 ;
        return getNodesNum(root);
    }
};

完全二叉树

利用了完全二叉树的特性,不用遍历没有必要的节点。

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。【只允许最后一层有空缺结点且空缺在右边

img

完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满

情况1:一个层数为k 的满二叉树总结点数为:(2^k)-1。因此满二叉树的结点树一定是奇数个。*直接用 2^树深度 - 1 来计算,注意这里根节点深度为1

对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。

222.完全二叉树的节点个数222.完全二叉树的节点个数1

可以看出如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量。

这里关键在于如何去判断一个左子树或者右子树是不是满二叉树呢

在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。如图:img

在完全二叉树中,如果递归向左遍历的深度不等于递归向右遍历的深度,则说明不是满二叉树,如图:

img

那有录友说了,这种情况,递归向左遍历的深度等于递归向右遍历的深度,但也不是满二叉树,如图:

img

如果这么想,大家就是对 完全二叉树理解有误区了,以上这棵二叉树,它根本就不是一个完全二叉树

判断其子树是不是满二叉树,如果是则利用公式计算这个子树(满二叉树)的节点数量,如果不是则继续递归,那么 在递归三部曲中,第二部:终止条件的写法应该是这样的:

if (root == nullptr) return 0; 
// 开始根据左深度和右深度是否相同来判断该子树是不是满二叉树
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
while (left) {  // 求左子树深度
    left = left->left;
    leftDepth++;
}
while (right) { // 求右子树深度
    right = right->right;
    rightDepth++;
}
if(leftDepth == rightDepth){
    return  (2<< leftDepth) - 1;// 注意(2<<1) 相当于2^2,返回满足满二叉树的子树节点数量
}

总体代码:

class Solution {
public:
    int getNodesNum(TreeNode* cur){
        if(cur == nullptr) return 0;//终止条件
        TreeNode* left = cur->left;
        TreeNode* right = cur->right;
        int leftDepth = 0;
        int rightDepth = 0;
        while(left){
            left = left->left;
            leftDepth++;
        }
        while(right){
            right = right->right;
            rightDepth++;
        }
        if(rightDepth == leftDepth){
            return (2<< leftDepth) - 1;//满足满二叉树节点的节点数
        }
        /*
        int leftTreeNum = getNodesNum(root->left);       // 左
        int rightTreeNum = getNodesNum(root->right);     // 右
        int result = leftTreeNum + rightTreeNum + 1;    // 中
        return result;
        */
        return getNodesNum(cur->left) + getNodesNum(cur->right) + 1;//不满足的就继续递归
    }

    int countNodes(TreeNode* root) {
        if(root == nullptr) return 0 ;
        return getNodesNum(root);
    }
};

总结

1.又熟悉了完全二叉树、满二叉树的定义。

什么是完全二叉树?只允许最后一层有空缺结点且空缺在右边的二叉树。完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满

什么是满二叉树?除最后一层无任何子节点外,每一层上的所有结点都有两个子结点二叉树。

国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。

2.相比普通的二叉树方法,**利用完全二叉树的特性,不用遍历那些没有必要的节点。**判断左右节点是否满足满二叉树的条件,满足的话用公式返回子树下面的总节点数量,不满足,就继续递归。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安装和使用 Oracle Database 23c 容器鏡像

Oracle Database 23c 是 Oracle 最新的数据库版本&#xff0c;它带来了许多新特性和性能改进。 对于开发者来说&#xff0c;Oracle 提供了一个免费的开发者版&#xff0c; 可以通过 Docker 容器轻松安装和使用。以下是详细的安装和使用指南。 安装 Docker 在开始之前&#xff0…

FME学习之旅---day17

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 【FME-HOW-TO系列】28 栅格邻域函数 RasterConvolver转换器说明&#xff1a; 接受包含栅格几何对象的输入要素&#xff0c;并在对所有波段应用卷积滤波 器后输出要素。 本人对栅格数据处理的较…

npm mongoose包下载冲突解决之道

我在新电脑下载完项目代码后,运行 npm install --registryhttps://registry.npm.taobao.org 1运行就报错&#xff1a; npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: lowcode-form-backend1.0.0 npm …

Find Any File (FAF) for Mac:您的专属文件搜索神器

在数字时代&#xff0c;我们的Mac硬盘中堆积着各式各样的文件&#xff0c;从工作文档到家庭照片&#xff0c;从音乐视频到学习资料&#xff0c;无一不体现出我们的生活和工作的丰富多彩。然而&#xff0c;当我们需要快速找到某个特定文件时&#xff0c;却常常在茫茫文件海中迷失…

送朋友的生日祝福静态页面代码!(小白也能轻松GET!)

Hey亲爱的小白们&#xff01;&#x1f44b; 知道你们想给朋友一个独特又有心的生日祝福&#xff0c;却苦于没有编程基础吗&#xff1f;别担心&#xff0c;来白嫖&#xff01;&#x1f381; &#x1f680;【生日祝福静态页面代码】来啦&#xff01;只需简单几步&#xff0c;就能…

Redis 的慢日志

Redis 的慢日志 Redis 的慢日志&#xff08;Slow Log&#xff09;是用于记录执行时间超过预设阈值的命令请求的系统。慢日志可以帮助运维人员和开发人员识别潜在的性能瓶颈&#xff0c;定位那些可能导致 Redis 性能下降或响应延迟的慢查询。以下是 Redis 慢日志的相关细节&…

【LV16 day1 自动mknod】

一、起源 仅devfs&#xff0c;导致开发不方便以及一些功能难以支持&#xff1a; 热插拔 不支持一些针对所有设备的统一操作&#xff08;如电源管理&#xff09;不能自动mknod用户查看不了设备信息设备信息硬编码&#xff0c;导致驱动代码通用性差&#xff0c;即没有分离设备和…

Web前端—(原生JS)歌词滚动效果

歌词滚动效果实现 歌词滚动效果HTML部分CSS部分JS部分解析歌词字符串&#xff0c;得到歌词的对象数组计算在当前情况下&#xff0c;播放器播放到第几秒的情况创建歌词元素设置ul元素的偏移量最后对时间变化的事件进行监听完整JS代码 歌词滚动效果 实现效果如图所示&#xff1a…

防静电工作台:静电敏感环境下的必备设备

在当今的电子制造、精密仪器制造、化学实验室等领域&#xff0c;静电敏感性已成为一个不可忽视的问题。静电可能对设备和操作人员造成严重损害&#xff0c;因此防止静电的产生和传导是至关重要的。在这样的背景下&#xff0c;防静电工作台应运而生&#xff0c;成为这些领域中不…

【蓝桥杯省赛真题37】python农田划分 中小学青少年组蓝桥杯比赛 算法思维python编程省赛真题解析

目录 python农田划分 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python农田划分 第十三届蓝桥杯青少年组python比赛省赛真题 一、题目要求…

kafka学习笔记02(小滴课堂)

Kafka命令行生产者发送消息和消费者消费消息实战 已存在的kafka不能重复创建。 broker设置的是1&#xff0c;factor大于broker了&#xff0c;所以报错。 生产者发送消息&#xff1a; kafka列表出现了新的kafka。 我们使用这个kafka。 我们启动消费者&#xff1a; 我们现在不从…

【dll解密】Dll加壳保护方案分析修复

分析背景 NGame游戏海外版出现了破解版&#xff0c;该版本在dump出游戏的dll中不能直接通过反编译工具查看修改后的游戏代码&#xff0c;导致无法确定外挂修改的直接逻辑点。本文主要针对AssemblyCSharp.dll模版&#xff0c;分析其dll保护的方法。 分析过程 1、拿到Encrypt_As…

【动手学深度学习-pytorch】 9.4 双向循环神经网络

在序列学习中&#xff0c;我们以往假设的目标是&#xff1a; 在给定观测的情况下 &#xff08;例如&#xff0c;在时间序列的上下文中或在语言模型的上下文中&#xff09;&#xff0c; 对下一个输出进行建模。 虽然这是一个典型情景&#xff0c;但不是唯一的。 还可能发生什么其…

【LVGL-使用SquareLine Studio设计器 】

LVGL-使用SquareLine Studio设计器 ■ 简介■ 安装■ SquareLine Studio移植到工程 ■ 简介 SquareLine Studio 设计器是一个付费软件。 ■ 安装 SquareLine Studio 设计器的下载地址 我们点击“WINDOWS”下载 SquareLine Studio 设计器&#xff0c;下载完成之后我们就会得到…

GIS硬核入门,二维地图是如何使用WGS84坐标系来转换成墨卡托投影的xyz地图瓦片切片的详细原理

前言 二维地图一般分成两种&#xff0c;一种是简化的道路地图视图&#xff0c;一种是卫星拍摄的高清影像地图。 四种坐标概念理解&#xff1a; 经度和纬度&#xff0c;对应地球上唯一的一个点&#xff08;例如&#xff1a;Google 使用世界大地测量系统 WGS84 标准&#xff0…

Day49:WEB攻防-文件上传存储安全OSS对象分站解析安全解码还原目录执行

目录 文件-解析方案-目录执行权限&解码还原 目录执行权限 解码还原 文件-存储方案-分站存储&OSS对象 分站存储 OSS对象存储 知识点&#xff1a; 1、文件上传-安全解析方案-目录权限&解码还原 2、文件上传-安全存储方案-分站存储&OSS对象 文件-解析方案-目…

数据结构之二叉树由浅入深(四)

目录 题外话 正题 第一题 第一题思路 第一题代码详解 第二题 第二题思路 第二题代码详解 第三题 第三题思路 第三题代码及详解 第四题 第四题思路 第四题代码及详解 第五题 第五题思路 第五题代码及详解 题外话 本来昨天就想写完这篇文章,怎么样是不是很大胆?…

ttkbootstrap界面美化系列之Notebook(四)

在简单的界面设计中&#xff0c;Notebook也是常用的组件之一&#xff0c;Notebook组件的引入可以根据标签来切换不同的界面。使得界面更有层次感&#xff0c;不必都挤在一个界面上。在tkinter中就有Notebook组件&#xff0c;在ttkbootstrap中&#xff0c;同样也对Notebook进行了…

Flutter开发之objectbox

Flutter开发之objectbox 在之前进行iOS开发的时候使用WCDB去进行管理数据库很方便&#xff0c;它支持ORM&#xff08;Object-Relational Mapping&#xff0c;对象关系映射&#xff09;&#xff0c;用于实现面向对象编程语言里不同类型系统的数据之间的转换。 那么在Flutter开发…

d3dcompiler_43.dll丢失的解决方法,快速解决win10系统错误问题

当系统提示“d3dcompiler_43.dll缺失”时&#xff0c;意味着计算机中缺少这一关键性动态链接库文件。该文件作为DirectX 3D编译器组件的一部分&#xff0c;对于许多依赖于DirectX技术的应用程序或游戏至关重要。这个错误通常会导致游戏或应用程序无法正常运行。为了解决这个问题…