代码随想录算法训练营第二十一天|530.二叉搜索树的最小绝对差、 501.二叉搜索树中的众数 、236. 二叉树的最近公共祖先

530.二叉搜索树的最小绝对差 

题目链接/文章讲解: 代码随想录

视频讲解:二叉搜索树中,需要掌握如何双指针遍历!| LeetCode:530.二叉搜索树的最小绝对差_哔哩哔哩_bilibili

1.方法1

1.1分析及思路

了解到差值最小的数为,结点左孩子然后一直向右的叶子结点,或者是右孩子一直向左的叶子结点。

对每一个结点都进行这样的判断。所以就用到了递归。

递归三部曲:

1.功能,参数,返回值

功能:判断结点的最小差值

参数:保存最小值的int*地址,以及结点

返回值:没有必要向上层返回值,所以不需要返回值

2.递归终止条件

假设传入一个非空结点,要进行的操作就是,寻找其前后最接近的值。所以只有NULL是才会结束循环。

3.单层递归逻辑

拿到一个结点,要进行寻找其左孩子的最右结点,以及右孩子的最左结点,然后求最小差值。

1.2代码及注释

void LETF(struct TreeNode* root,int* Min) {
    // 定义一个指向左子树的节点Node
    struct TreeNode* Node = root->left;
    // 遍历左子树
    while(Node!=NULL){
        // 如果Node的右子节点为空,则说明找到了,左孩子的最右侧的叶子结点
        if(Node->right == NULL)
            // 更新最小值Min
            if(*Min > root->val - Node->val)
                *Min = root->val - Node->val;
        Node = Node->right;
    }
}

void RIGHT(struct TreeNode* root,int* Min) {
    // 定义一个指向右子树的节点Node
    struct TreeNode* Node = root->right;
    // 遍历右子树
    while(Node!=NULL){
        // 如果Node的左子节点为空,则说明找到了右孩子的最左侧叶子结点
        if(Node->left == NULL)
            // 更新最小值Min
            if(*Min > Node->val - root->val)
                *Min = Node->val - root->val;
        Node = Node->left;
    }
}

void traversal(struct TreeNode* root,int* Min) {
    // 如果当前节点为空,直接返回
    if(root == NULL) return;
    if(root->left != NULL)
        LETF(root,Min);//寻找左孩子的最右侧叶子结点
    if(root->right != NULL)
        RIGHT(root,Min);//寻找右孩子的最左侧结点
    // 递归遍历左子树
    traversal(root->left,Min);
    // 递归遍历右子树
    traversal(root->right,Min);
}

int getMinimumDifference(struct TreeNode* root) {
    // 初始化最小值为INT_MAX
    int Min = INT_MAX;
    // 从根节点开始遍历
    traversal(root,&Min);
    // 返回最小值
    return Min;
}

2.方法2

2.1分析及思路

与98.验证二叉搜索树思路类似,用两个指针进行遍历,这俩个指针的差值是可能成为最小差值的,因为中序遍历是会得到一个递增的数组的。

1.功能、参数、返回值

功能:得到最小的差值

参数:保存最小差值的Min、根节点root、访问过的前一个结点pre

返回值:下一侧递归不需要及时返回信息,所以不需要返回值

2.终止条件

当访问到NULL时说明该树已经访问完毕。所以终止条件为NULL

3.单层递归的逻辑

拿到一个结点root,用root-pre判断差值,然后递归的访问其左子树,右子树

2.2代码及分析

// 定义一个函数用于遍历二叉搜索树
void traversal(struct TreeNode* root, struct TreeNode* *pre, int* Min) {
    // 如果当前节点为空,直接返回
    if(root == NULL) return;
    
    // 递归遍历左子树
    traversal(root->left, pre, Min);
    
    // 如果前一个节点不为空
    if(*pre != NULL){
        // 更新最小差值
        if(*Min > (root->val - (*pre)->val)){
            *Min = (root->val - (*pre)->val);
        }
    }
    
    // 更新前一个节点为当前节点
    *pre = root;
    
    // 递归遍历右子树
    traversal(root->right, pre, Min);
}

// 定义一个函数用于获取二叉搜索树中节点值的最小差值
int getMinimumDifference(struct TreeNode* root) {
    // 初始化最小差值为最大整数值
    int Min = INT_MAX;
    
    // 初始化前一个节点为空
    struct TreeNode* pre = NULL;
    
    // 调用遍历函数
    traversal(root, &pre, &Min);
    
    // 返回最小差值
    return Min;
}

501.二叉搜索树中的众数 

代码随想录

视频讲解:不仅双指针,还有代码技巧可以惊艳到你! | LeetCode:501.二叉搜索树中的众数_哔哩哔哩_bilibili

1.分析及思路

最直观的想法就是,访问一遍树,把所有的数出现的频率都统计下来,然后再把频率高的数进行返回。

进行优化,对于二叉搜索树而言,中序遍历会得到一个不减的数组,和上一题类似,使用双指针进行频率的统计,如果当前频率等于最大频率,则说明众数有多个,在结果数组的末尾加上它即可。若大于则说明,已经统计的结果全部作废,要重新统计,即再次从数组下标为0的地方存储结果。

运用了中序遍历,所以可以使用递归法,递归三部曲:

1.功能,参数,返回值

功能:统计二叉树中的众数

参数:需要传入一个根节点,所以有struct TreeNode* root

因为是双指针,所以需要一个struct TreeNode** pre,保存上次访问过的结点

最终返回的是一个数组,所以穿进来一个int* result

以及统计数组的大小的int* returnSize

需要当前频率和最大频率做对比,所以有int* MaxCount,int* NowCount。

2.递归终止条件

很明显,遇到空结点时,没办法统计所以终止。

3.单层递归逻辑

遇到一个结点,看上次访问的结点与它是否相等,相等则频率加1。若不想等则说明,此时访问的是新出现的,此时频率设置为1。若该频率与最大频率相等,则把它加入结果数组的末尾。若大于,则说明,结果数组中的众数全部作废,重新统计。

2.代码及注释

void traversal(struct TreeNode* root, struct TreeNode** pre, int* returnSize, int* result, int* MaxCount, int* NowCount) {
    // 如果当前节点为空,直接返回
    if(root == NULL) return;
    
    // 递归遍历左子树
    traversal(root->left, pre, returnSize, result, MaxCount, NowCount);
    
    // 如果前一个节点为空,则当前节点是第一个节点,NowCount置为1
    if(*pre == NULL) *NowCount = 1;
    // 如果前一个节点的值等于当前节点的值,则当前节点是重复节点,NowCount加1
    else if((*pre)->val == root->val) (*NowCount)++;
    // 如果前一个节点的值不等于当前节点的值,则当前节点是新节点,NowCount置为1
    else *NowCount = 1;
    
    // 更新pre指针为当前节点
    *pre = root;
    
    // 如果NowCount等于MaxCount,将当前节点的值加入结果数组并增加返回大小
    if(*NowCount == *MaxCount) result[(*returnSize)++] = root->val;
    // 如果NowCount大于MaxCount,更新MaxCount为NowCount,重置返回大小为1,将当前节点的值放入结果数组
    else if(*NowCount > *MaxCount){
        *MaxCount = *NowCount;
        *returnSize = 1;
        result[0] = root->val;
    }
    
    // 递归遍历右子树
    traversal(root->right, pre, returnSize, result, MaxCount, NowCount);
}

int* findMode(struct TreeNode* root, int* returnSize) {
    // 初始化返回大小为0
    *returnSize = 0;
    
    // 分配结果数组的内存空间
    int* result = (int*)malloc(sizeof(int)*10001);
    
    // 初始化前一个节点指针、最大出现次数、当前出现次数为0
    struct TreeNode* pre = NULL;
    int MaxCount = 0;
    int NowCount = 0;
    
    // 调用traversal函数进行遍历
    traversal(root, &pre, returnSize, result, &MaxCount, &NowCount);
    
    // 返回结果数组
    return result;
}

236. 二叉树的最近公共祖先  

代码随想录

视频讲解:自底向上查找,有点难度! | LeetCode:236. 二叉树的最近公共祖先_哔哩哔哩_bilibili

1.分析及思路

很明显,要想知道最近公共祖先结点,就要从该结点左右子树中找到qp两个结点。也就是先访问其左子树,再访问其右子树,也就是使用的是后续遍历。即找到结点后就向父结点返回。

递归三部曲:

1.功能、参数、返回值

功能:返回最近公共祖先

参数:根节点、p、q

返回值:因为需要向父结点返回有没有该结点,所以需要返回值

2.递归终止条件

当传入NULL时,很明显终止。当找到该结点时,也应该终止。

3.单层递归逻辑

遇到结点时,先访问其左子树,在访问其右子树,才能得到该结点是不是有,目标结点。

1.若左子树有目标结点,右子树也有目标结点,则说明它就是最近公共祖先。

2.若右子树有,左子树没有,则说明最近公共祖先在右侧,即返回右侧返回的结点

3.左子树有,右子树没有,则返回左子树返回的值

4.若都没有很明显,返回NULL

2.代码及注释

// 定义一个函数,用于找到两个节点的最近公共祖先
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    // 如果根节点为空,返回空
    if(root == NULL) return root;
    // 如果根节点等于其中一个节点,直接返回根节点
    if(root == q || root == p) return root;
    // 递归查找左子树中的最近公共祖先
    struct TreeNode* left = lowestCommonAncestor(root->left,p,q);
    // 递归查找右子树中的最近公共祖先
    struct TreeNode* right = lowestCommonAncestor(root->right,p,q);
    // 如果左右子树都找到了最近公共祖先,则返回根节点
    if(left != NULL && right != NULL) return root;
    // 如果左子树找到了最近公共祖先,则返回左子树的最近公共祖先
    if(left == NULL && right != NULL) return right;
    // 如果右子树找到了最近公共祖先,则返回右子树的最近公共祖先
    if(left != NULL && right == NULL) return left;
    // 如果左右子树都没有找到最近公共祖先,则返回空
    return NULL;
}

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

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

相关文章

DevEco Studio下载与安装(Windows)

下载地址: HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 安装时直接点击 next 即可。 运⾏已安装的DevEco Studio,⾸次使⽤,请选择Do not import settings,单击OK。 1.安装Node.js 如果本地有下载,可以…

C程序的编译过程

目录 一、GCC编译器 二、编译过程 1、预处理(Preprocessing) 2、编译(Compilation) 3、汇编(Assembly) 4、链接(Linking) 三、秋招真题演练 一、GCC编译器 在这里&#xf…

linux系统Jenkins工具web配置

Jenkins工具配置 插件配置系统配置系统工具配置 插件配置 下载 Maven Integration Pipeline Maven lntegration gitlab Generic webhook Trigger nodejs Blue ocean系统配置 系统配置结束系统工具配置

androidframework开发面试,阿里P8成长路线

字节跳动Android面经 一面问的 Java 和 Android 基础 1、Jvm虚拟机 2、messageQueue会不会阻塞ui线程 3、对象锁和类锁 4、之字形打印树 5、还有其他的 《Android学习笔记总结最新移动架构视频大厂安卓面试真题项目实战源码讲义》 **完整开源项目:docs.qq.com/doc…

Linux命名管道

Linux匿名管道-CSDN博客 目录 1.原理 2.接口实现 3.模拟日志 Linux匿名管道-CSDN博客 这上面叫的是匿名管道,不要将两者搞混,匿名管道说的是两个有血缘关系的进程相互通信,但是命名管道就是两个没有关系的管道相互通信。 1.原理 和匿名…

解密犯罪时间 - 华为OD统一考试(C卷)

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C 题目描述 警察在侦破一个案件时,得到了线人给出的可能犯罪时间,形如 HH:MM 表示的时刻。 根据警察和线人的约定,为了隐蔽,该…

RabbitMQ实战学习

RabbitMQ实战学习 文章目录 RabbitMQ实战学习RabbitMQ常用资料1、安装教程2、使用安装包3、常用命令4、验证访问5、代码示例 一、RabbitMQ基本概念1.1. MQ概述1.2 MQ 的优势和劣势1.3 MQ 的优势1. 应用解耦2. 异步提速3. 削峰填谷 1.4 MQ 的劣势1.5 RabbitMQ 基础架构1.6 JMS 二…

图形系统开发实战课程:进阶篇(上)——8.图形样式

图形开发学院|GraphAnyWhere 课程名称:图形系统开发实战课程:进阶篇(上)课程章节:“图形样式”原文地址:https://www.graphanywhere.com/graph/advanced/2-8.html 第八章 图形样式 1. 填充和描边 \quad 在图形系统实战…

vision mamba 运行训练记录,解决bimamba_type错误

下载vision mamba github上的项目后,解压,进入文件夹项目,然后配环境 unzip Vim-main.zip cd Vim-mainconda create -n mamba python3.10.13conda activate mamba pip install torch2.1.1 torchvision0.16.1 torchaudio2.1.1 --index-url ht…

java面试(网络)

TCP和UDP有什么区别?TCP三次握手不是两次? TCP:面向连接,可靠的,传输层通信协议。点对点,占用资源多,效率低。 UDP:无连接,不可靠,传输层通信协议。广播&…

照片中不想要的部分怎么去除?教你几个小妙招

照片在我们的生活中占据着无可替代的地位。它们是我们生活的缩影,定格住我们的瞬间,记录着我们的记忆,让我们可以随时随地回顾过去的美好时光。无论是家庭聚会、旅行、婚礼还是其他重要的场合,我们都会用照片来捕捉这些珍贵的时刻…

springboot 实现本地文件存储

springboot 实现本地文件存储 实现过程 上传文件保存文件(本地磁盘)返回文件HTTP访问服务器路径给前端,进行效果展示 存储 服务端接收上传的目的是提供文件的访问服务,对于SpringBoot而言,其对静态资源访问提供了很…

【Java设计模式】三、

文章目录 0、案例:咖啡屋1、简单工厂模式 静态工厂(不属于23种之列)2、工厂方法模式3、抽象工厂模式4、建造者模式5、原型设计模式 0、案例:咖啡屋 模拟咖啡店点餐。咖啡有多种,抽象类,子类为各种咖啡。咖…

element ui富文本编辑器的使用(quill-editor)

引用组件 <el-form-item label"内容"><editor v-model"obj.activity_content" :min-height"192"/> </el-form-item> 组件封装 <template><div><el-upload:action"uploadUrl":before-upload"…

Node.js如何进行性能监控和分析

Node.js作为一款流行的后端开发技术&#xff0c;其性能监控和分析对于保证系统稳定性和性能优化至关重要。在本文中&#xff0c;我们将探讨Node.js如何进行性能监控和分析&#xff0c;以便开发者能够更好地了解系统运行状况并进行必要的优化。 为什么进行性能监控和分析&#…

力扣刷题:141.环形链表

题目&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中…

idea集成git详解教程(实用篇)

0.Git常用命令 Git常用命令-CSDN博客 1.下载git Git - Downloads 一路傻瓜式安装即可&#xff08;NEXT&#xff09; 2.软件测试 在Windows桌面空白处&#xff0c;点击鼠标右键&#xff0c;弹出右键菜单 Git软件安装后&#xff0c;会在右键菜单中增加两个菜单 Git GUI He…

GTID的使用原理

一.简介 GTID是MySQL 5.6的新特性&#xff0c;其全称是Global Transaction Identifier&#xff0c;可简化MySQL的主从切换以及Failover。GTID用于在binlog中唯一标识一个事务。当事务提交时&#xff0c;MySQL Server在写binlog的时候&#xff0c;会先写一个特殊的Binlog Event&…

机器学习提升秘籍:Scikit-learn学习网站全攻略!

介绍&#xff1a;是一个开源的Python机器学习库&#xff0c;它提供了一整套用于数据挖掘和数据分析的工具&#xff0c;包括各种分类、回归、聚类和降维算法以及模型评估、选择和数据预处理等功能。以下是关于Scikit-learn的一些详细介绍&#xff1a; 算法覆盖广泛&#xff1a;S…

基于SSM SpringBoot vue服装物流管理系统

基于SSM SpringBoot vue服装物流管理系统 系统功能 首页 图片轮播 人个中心 登录注册 后台管理: 登录注册 个人中心 货物信息管理 货物入库管理 订单信息管理 商品出库管理 快递追踪管理 用户管理 供应商信息管理 盘点信息管理 管理员管理 开发环境和技术 开发语言&#xf…