24暑假算法刷题 | Day18 | LeetCode 530. 二叉搜索树的最小绝对差,501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先

目录

  • 530. 二叉搜索树的最小绝对差
    • 题目描述
    • 题解
  • 501. 二叉搜索树中的众数
    • 题目描述
    • 题解
  • 236. 二叉树的最近公共祖先
    • 题目描述
    • 题解


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

点此跳转题目链接

题目描述

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值

差值是一个正数,其数值等于两值之差的绝对值。

示例 1:

在这里插入图片描述

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

示例 2:

在这里插入图片描述

输入:root = [1,0,48,null,null,12,49]
输出:1

提示:

  • 树中节点的数目范围是 [2, 104]
  • 0 <= Node.val <= 105

题解

暴力解法把所有节点两两相减、求绝对值最小的差即可,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

我们可以利用二叉搜索树的一个重要性质来优化算法:

  • 二叉搜索树的中序遍历结果是一个从小到大的有序数列

此处默认节点左子树中各节点值小于节点、右子树中各节点值大于节点,则上面的性质是显然的。

也就是说,如果我们先获得中序遍历结果,再从头到尾依次两两相减,只需要遍历一次就可以得出最小绝对差。

由于中序遍历结果是递增序列,最小的任意两数之差肯定出现在相邻的两数之间;且后一个数大于前一个数,所以总让后一个数减去前一个数,所得结果必为正数,不必再求绝对值。

显然,这一算法可以直接在中序遍历的过程中运行,C++代码如下:

int res = INT_MAX;
TreeNode *pre; // 暂存中序遍历时前一个节点的指针void traversal(TreeNode *root)
{if (root->left)traversal(root->left); // 左if (pre)res = min(res, root->val - pre->val); // 中pre = root;                               // 更新前指针if (root->right)traversal(root->right); // 右
}int getMinimumDifference(TreeNode *root)
{traversal(root);return res;
}

501. 二叉搜索树中的众数

点此跳转题目链接

题目描述

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树

示例 1:

在这里插入图片描述

输入:root = [1,null,2,2]
输出:[2]

示例 2:

输入:root = [0]
输出:[0]

提示:

  • 树中节点的数目在范围 [1, 104]
  • -105 <= Node.val <= 105

进阶: 你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)

题解

既然是找众数,那么将二叉树遍历一遍、记录每个数出现的频率,是必不可少的了。常规思路应该是:

  • 遍历该树,同时记录节点值的出现频率

  • 找到最大的出现频率

  • 将出现频率最大的数加入结果集

    ⚠️ 注意题目说了,众数可能不止一个

当然,后两步可以合并:当发现更大的频率的时候,先将现有结果集清空,再加入新的最高频数字。这种方法的C++代码如下:

class Solution
{
public:unordered_map<int, int> freqMap; // 用一个哈希表记录每个数值出现的频率int maxShowTime = -1;// 中序遍历void traversal(TreeNode *cur){if (cur->left)traversal(cur->left); // 左freqMap[cur->val]++;      // 中if (cur->right)traversal(cur->right); // 右}vector<int> findMode(TreeNode *root){traversal(root);vector<int> res;for (const auto &pair : freqMap){if (pair.second > maxShowTime){maxShowTime = pair.second;res.clear(); // 出现更高频的元素,将原来的结果集清空res.push_back(pair.first);}else if (pair.second == maxShowTime)res.push_back(pair.first); // 众数可能不止一个}return res;}
};

不过,上述方法显然对任意树是通用的,意味着我们并没有利用到二叉搜索树的独特性质。同时,题目进阶要求也说了,可以不使用额外空间解决此题(上面的算法有个额外的哈希表空间开销)。

即是二叉搜索树,又要遍历之,自然联想到性质:

  • 二叉搜索树的中序遍历结果是一个从小到大的有序数列

所以,我们可以采用中序遍历的方法:

  • 如果当前节点值和上一个节点值相同,则当前频率加1;否则,说明遍历到一个新数值的节点,将当前频率重置为1
  • 如果当前频率等于最大频率,则将当前节点值加入结果集
  • 如果当前频率大于最大频率,则更新最大频率,清空原来结果集,将当前节点值接入结果集

这样,就避免了额外的空间开销:

class Solution
{
public:int maxShowTime = -1;int curShowTime = 0;TreeNode *pre = nullptr; // 中序遍历序列中,当前节点的前一个节点vector<int> res;// 中序遍历void traversal(TreeNode *cur){// 左if (cur->left)traversal(cur->left); // 中if (!pre)curShowTime = 1; // 第一个节点else if (cur->val == pre->val)curShowTime++; // 相同的节点值elsecurShowTime = 1; // 新的节点值pre = cur; // 更新前节点指针if (curShowTime == maxShowTime)res.push_back(cur->val);if (curShowTime > maxShowTime) {maxShowTime = curShowTime; // 更新最大出现频率res.clear();               // 清除之前的结果res.push_back(cur->val);}// 右if (cur->right)traversal(cur->right);}vector<int> findMode(TreeNode *root){traversal(root);return res;}
};

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

点此跳转题目链接

题目描述

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

在这里插入图片描述

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

在这里插入图片描述

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

提示:

  • 树中节点数目在范围 [2, 105] 内。
  • -109 <= Node.val <= 109
  • 所有 Node.val 互不相同
  • p != q
  • pq 均存在于给定的二叉树中。

题解

根据题目描述,可以发现,符合直觉的思路是自底向上地查找——而这与后序遍历的遍历顺序相符。所以,我们考虑基于后序遍历,递归地返回 pq 的最近公共祖先。

用递归,首先考虑递归出口。我们想要返回的总是一个 p 和/或 q 的祖宗节点,所以以当前节点 root 的左右子树中必须有 p 和/或 q (自然,也有其祖宗节点),否则返回空节点:

if (!left && !right)return nullptr;

按照这种思路,节点不为空即说明该节点是 pq 或者它们之一/公共的祖宗节点

其余情况,即 root 的左右子树不都为空,说明它们之中有 p 和/或 q 及其祖宗节点:

1️⃣ 先看看最简单的情况:类似上面的示例1,当前节点 root 的左孩子 leftright 恰好就是 pq (对应关系无所谓),则 root 就是它们的最近公共祖先,此时返回 root 即可:

if (left && right)return root;

2️⃣ 如果当前节点是 pq ,也直接返回当前节点即可:题目说了 一个节点也可以是它自己的祖先

if (root == p || root == q)return root;

3️⃣ 如果 leftright 之间有且仅有一个不为空,则返回不为空的那个节点——该节点是 pq 或它们之一/公共的祖先:

相当于上面示例2中的节点 2

if (left && !right)return left;
if (!left && right)return right;

整体C++代码如下:

TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
{// 基于后序遍历if (!root)return nullptr;TreeNode *left = lowestCommonAncestor(root->left, p, q);   // 左TreeNode *right = lowestCommonAncestor(root->right, p, q); // 右// 中// 当前节点为p, q之一if (root == p || root == q)return root;// 否则,看左右孩子节点的情况else if (left && right)return root;else if (left && !right)return left;else if (!left && right)return right;else // 左右都为空return nullptr;
}

该算法更详细的讲解参见 代码随想录的本题题解 。

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

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

相关文章

Python 更换 pip 源详细指南

目录 前言pip 国内源临时换源方法一&#xff1a;添加参数方法二&#xff1a;设置环境变量 永久换源方法三&#xff1a;修改配置方法四&#xff1a;pip 命令修改 总结 前言 在我们使用 Python 3 时&#xff0c;pip 是一个不可或缺的工具&#xff0c;它用于安装和管理第三方库。…

在虚拟机 CentOS7 环境下安装 MySQL5.7 数据库

配置目标 在虚拟机的 Linux CentOS7 环境下安装 MySQL5.7 版数据库&#xff0c;并能从宿主机 Windows 系统连接该数据库&#xff08;默认端口&#xff1a;3306&#xff09;。 1. 准备工作 WMware 虚拟机&#xff1a;VMware Workstation 16 ProCentOS7 镜像&#xff1a;CentO…

基于密钥的身份验证(Linux-Linux)

A主机&#xff1a; 1、生成密钥对 [rootservera ~]# ssh-keygen查看公钥 注&#xff1a;id_rsa为私钥&#xff08;证书&#xff09;&#xff0c;id_rsa.pub为公钥 2、注册公钥到服务器 [rootservera ~]# ssh-copy-id root172.25.250.106 查看.ssh 3、使用密钥连接服务器 #…

【yolov8】3、yolov8 环境安装 【GPU版】

pycharm下载安装 yolov8 环境安装 【GPU版】 1、要求1.1 什么是 CUDA 和 CUDNN1.2 查看cuda版本的3种方法&#xff08;版本在10.2以上的可以忽略本章节&#xff09;&#xff1a;1.3 没有找到NIVDIA图标&#xff0c;确认是否有英伟达显卡 2、pycharm下载安装进入官网 3、yolov8…

【Android】视图与常用控件总结

文章目录 一、视图基础1.1 设置视图的宽高1.2 设置视图的间距1.3 设置视图的对齐方式1.4 总结 二、控件2.1 TextView2.1.1 设置宽高2.1.2 设置内容2.1.3 设置大小2.1.4 设置颜色 2.2 Button2.3 EditText2.4 ImageView2.5 ProgressBar2.6 AlertDialog2.7 ProgressDialog 本文主要…

JAVASE进阶day14(网络编程续TCP,日志)

TCP 三次握手 四次挥手 package com.lu.day14.tcp;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket;public class Client {public static void main(String[] args) {try(Socket socket new Socket("192.…

哈默纳科HarmonicDrive减速机组装注意事项

在机械行业中&#xff0c;精密传动设备HarmonicDrive减速机对于维持机械运作的稳定性和高效性起着至关重要的作用。然而在减速机的组装过程中&#xff0c;任何一个细微的错误都可能导致其运转时出现振动、异响等不良现象&#xff0c;严重时甚至可能影响整机的性能。因此&#x…

【开源库】libodb库编译及使用

前言 本文介绍windows平台下libodb库的编译及使用。 文末提供libodb-2.4.0编译好的msvc2019_64版本&#xff0c;可直接跳转自取 ODB库学习相关 【开源库学习】libodb库学习&#xff08;一&#xff09; 【开源库学习】libodb库学习&#xff08;二&#xff09; 【开源库学习】…

30种图像动画特效算法(C#多线程版) - 好文要转

最近想做一个屏幕保护软件&#xff0c;需要图片切换效果&#xff0c;于是就找到这个博文&#xff0c;强烈推荐&#xff1a; https://blog.51cto.com/mengliao/473169 其中的源码包在此下载&#xff1a;https://download.csdn.net/download/lzhdim/89532212 效果如下&#xff1a…

Pycharm 安装与使用

PyCharm的安装与使用 一、什么是PyCharm PyCharm是由JetBrains开发的专业Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;提供智能代码补全、语法高亮和代码导航等编辑功能。它具有强大的调试工具和内置版本控制系统支持&#xff0c;方便代码管理和协作。PyCharm…

《小程序02:云开发之增删改查》

一、前置操作 // 一定要用这个符号包含里面的${}才会生效 wx.showToast({title: 获取数据成功&#xff1a;${colorLista}, })1.1&#xff1a;初始化介绍 **1、获取数据库引用&#xff1a;**在开始使用数据库 API 进行增删改查操作之前&#xff0c;需要先获取数据库的引用 cons…

<数据集>安全帽佩戴识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;3912张 图片分辨率&#xff1a;640640 标注数量(xml文件个数)&#xff1a;3912 标注数量(txt文件个数)&#xff1a;3912 标注类别数&#xff1a;2 标注类别名称&#xff1a;[no-helmet, helmet] 序号类别名称图片…

机器学习——降维算法PCA和SVD(sklearn)

目录 一、基础认识 1. 介绍 2. 认识 “ 维度 ” &#xff08;1&#xff09;数组和Series &#xff08;2&#xff09;DataFrame 表 &#xff08;3&#xff09;图像 3. 降维思想 4. 降维步骤 二、降维算法&#xff08;PCA&#xff09; 1. PCA实现 &#xff08;1&#…

java转义文本中的HTML字符为安全的字

java转义文本中的HTML字符为安全的字 &#xff0c;以下字符被转义&#xff1a;替换为 (&apos; doesnt work in HTML4) " 替换为 &quot; & 替换为 &amp; < 替换为 < > 替换为 >1.先添加hutool依赖到pom <dependency><groupId>cn…

VSCode部署Pytorch机器学习框架使用Anaconda(Window版)

目录 1. 配置Anaconda1.1下载安装包1. Anaconda官网下载2, 安装Anaconda 1.2 创建虚拟环境1.3 常用命令Conda 命令调试和日常维护 1.4 可能遇到的问题执行上述步骤后虚拟环境仍在C盘 2. 配置cuda2.1 查看显卡支持的cuda版本2.2 下载对应cuda版本2.3 下载对应的pytorch可能出现的…

【数据结构】二叉树OJ题_对称二叉树_另一棵的子树

对称二叉树 题目 101. 对称二叉树 - 力扣&#xff08;LeetCode&#xff09; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2…

【ECharts】使用 ECharts 处理不同时间节点的数据系列展示

使用 ECharts 处理不同时间节点的数据系列展示 在数据可视化中&#xff0c;我们经常遇到这样的问题&#xff1a;不同数据系列的数据点在时间轴上并不对齐。这种情况下&#xff0c;如果直接在 ECharts 中展示&#xff0c;图表可能会出现混乱或不准确。本文将通过一个示例代码&a…

C#与C++交互开发系列(四):使用C++/CLI进行互操作

欢迎来到C#与C交互开发系列的第四篇。在这篇博客中&#xff0c;我们将深入探讨使用C/CLI进行互操作的方法和技巧。C/CLI&#xff08;Common Language Infrastructure&#xff09;是C与.NET框架之间的桥梁&#xff0c;能够让C代码与托管代码无缝集成。 4.1 什么是C/CLI&#xf…

【源码阅读】Sony的go breaker熔断器源码探究

文章目录 背景源码分析总结 背景 在微服务时代&#xff0c;服务和服务之间调用、跨部门调用都是很常见的事&#xff0c;但这些调用都存在很多不确定因素&#xff0c;如核心服务A依赖的部门B服务挂掉了&#xff0c;那么A本身的功能将会受到直接的影响&#xff0c;而这些都会影响…

GaussianPro使用笔记

1. 介绍 GaussianPro: 3D Gaussian Splatting with Progressive Propagation 3D高斯分布(3DGS)最近以其高保真度和效率彻底改变了神经渲染领域。然而&#xff0c;3DGS在很大程度上依赖于运动结构&#xff08;SfM&#xff09;技术生成的初始化点云。当处理不可避免地包含无纹理…