算法日记day 20(二叉搜索树)

一、验证二叉搜索树

题目:

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左

    子树

    只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

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

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

 思路:

采用中序:左、中、右的方法,利用双指针,由于二叉搜索树的中序遍历是一个递增的序列,因此只需要比较相邻两个元素的值是否是符合递增的条件即可

代码:

TreeNode pre = null;public boolean isValidBST(TreeNode root) {if (root == null)return true;// 递归检查左子树boolean leftValue = isValidBST(root.left);// 检查当前节点值是否大于前一个节点值//pre != null表示在第一次遍历时只有一个指针的值被确立,只有在第二次遍历时才能记录两个元素并进行比较,因此第一次if判断不进行if (pre != null && pre.val >= root.val) {return false;}// 更新 pre 为当前节点pre = root;// 递归检查右子树boolean rightValue = isValidBST(root.right);// 返回左右子树是否都是 BSTreturn leftValue && rightValue;
}
  1. 变量 pre 的作用

    • pre 是一个全局变量,用来保存当前节点在中序遍历中的前一个节点。在中序遍历的过程中,通过比较当前节点与前一个节点的值来判断是否满足 BST 的性质(左子树的所有节点值均小于当前节点,右子树的所有节点值均大于当前节点)。
  2. isValidBST 方法

    • 方法签名为 boolean isValidBST(TreeNode root),返回一个布尔值,表示以 root 为根的子树是否是 BST。
    • 如果 root 为 null,则空树被视为 BST,直接返回 true
  3. 递归检查左子树

    • boolean leftValue = isValidBST(root.left); 递归调用 isValidBST 方法,检查左子树是否为 BST,并将结果保存在 leftValue 中。
  4. 检查当前节点值

    • if (pre != null && pre.val >= root.val) 检查当前节点 root 的值与 pre 的值(前一个节点的值)比较,如果不满足 BST 的性质(即当前节点值小于等于前一个节点的值),则返回 false
  5. 更新 pre

    • pre = root; 将 pre 更新为当前节点 root,以便在下一次递归中使用。
  6. 递归检查右子树

    • boolean rightValue = isValidBST(root.right); 递归调用 isValidBST 方法,检查右子树是否为 BST,并将结果保存在 rightValue 中。
  7. 返回结果

    • return leftValue && rightValue; 将左子树和右子树是否都为 BST 的结果进行逻辑与操作,最终确定以 root 为根的子树是否是 BST。

二、 二叉搜索树最小绝对差

题目:

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

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

示例 1:

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

示例 2:

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

思路:

与验证二叉搜索树类似,仍是采用中序遍历的方法,将节点的值按递增排序出来后相邻两值相减,记录最小的绝对差

代码:

int result = Integer.MAX_VALUE; // 初始化结果为整型最大值,用于保存最小差值
TreeNode pre = null; // 用来保存中序遍历中当前节点的前一个节点/*** 计算二叉搜索树中任意两个节点值的最小差值* @param root 当前子树的根节点* @return 最小差值*/
public int getMinimumDifference(TreeNode root) {if (root == null)return 0; // 如果根节点为空,返回 0,因为空树不存在差值// 递归处理左子树getMinimumDifference(root.left);// 计算当前节点与前一个节点的差值,并更新结果if (pre != null) {result = Math.min(result, root.val - pre.val);}// 更新前一个节点为当前节点pre = root;// 递归处理右子树getMinimumDifference(root.right);// 返回最小差值return result;
}
  1. 全局变量

    • result 初始化为整型最大值,用来存储找到的最小节点值差。
    • pre 用来跟踪中序遍历中的前一个节点。
  2. 方法 getMinimumDifference

    • 该方法计算给定二叉搜索树中任意两个节点值的最小差值。
    • root 参数表示当前子树的根节点。
  3. 空节点处理

    • 如果 root 为 null,表示当前子树为空树,直接返回 0,因为空树不存在节点差值。
  4. 递归左子树

    • getMinimumDifference(root.left); 递归调用,处理左子树。
  5. 计算节点差值

    • if (pre != null) { result = Math.min(result, root.val - pre.val); }
      • 如果 pre 不为 null,说明当前节点 root 不是最左侧节点,计算当前节点值与前一个节点值的差,并更新 result 为这两者之差的最小值。
  6. 更新前一个节点

    • pre = root; 更新 pre 为当前节点 root,为下一次递归调用做准备。
  7. 递归右子树

    • getMinimumDifference(root.right); 递归处理右子树。
  8. 返回结果

    • return result; 返回计算得到的最小差值。

 

三、 求二叉搜索树中的众数

题目:

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

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

假定 BST 满足如下定义:

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

示例 1:

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

示例 2:

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

思路:

用双指针的方法,在中序遍历得出的递增数组中找出出现频率最大的数,存入结果数组中,如果在遍历的过程中找到出现频率更大的数,则需要将结果数组中存入的数全部清空,同时将频率更高的数加入结果数组,直到遍历完毕

代码:

public void find(TreeNode root) {if (root == null)return; // 如果当前节点为空,直接返回find(root.left); // 递归处理左子树// 计数逻辑if (pre == null || root.val != pre.val) {count = 1; // 如果当前节点与前一个节点不同,重置计数器为1} else {count++; // 如果当前节点与前一个节点相同,计数器加1}// 更新众数列表和最大计数值if (count > maxCount) {resList.clear(); // 如果当前计数大于最大计数,清空之前的结果列表resList.add(root.val); // 将当前节点值加入结果列表maxCount = count; // 更新最大计数值} else if (count == maxCount) {resList.add(root.val); // 如果当前计数等于最大计数,将当前节点值加入结果列表}pre = root; // 更新前一个节点为当前节点find(root.right); // 递归处理右子树
}
  • find 方法是核心的递归方法,用于执行中序遍历并计算节点值的出现次数。
  • 如果 root 为 null,直接返回,结束递归。
  • 递归地处理左子树。
  • 根据中序遍历的顺序,判断当前节点值与前一个节点值是否相同,更新计数器 count
  • 根据计数器 count 的值更新 resList 中的节点值和 maxCount
  • 更新 pre 为当前节点,为下一次递归调用做准备。
  • 递归地处理右子树。
public int[] findMode(TreeNode root) {find(root); // 调用递归方法进行中序遍历int[] res = new int[resList.size()]; // 创建结果数组for (int i = 0; i < resList.size(); i++) {res[i] = resList.get(i); // 将结果列表中的值复制到结果数组}return res; // 返回结果数组
}
  • TreeNode pre = null;:用于保存中序遍历中的前一个节点。
  • int maxCount = 0;:记录出现最多次数的节点值的出现次数。
  • int count = 0;:当前节点值的出现次数。
  • ArrayList<Integer> resList = new ArrayList<>();:用于存储找到的所有众数的列表。
  • 首先调用 find(root) 方法来进行中序遍历,并找到众数。
  • 创建一个数组 res,将 resList 中的值复制到 res 中,并返回。

 

四、二叉树的最近公共祖先 

题目:

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

百度百科中最近公共祖先的定义为:“对于有根树 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

思路:

采用后序遍历,当左子树和右子树存在p和q时,返回其父节点为最近公共祖先,若左子树没有,右子树存在p和q,返回右子树节点,同理若右子树没有,左子树存在,返回左子树节点

代码:

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {// 如果当前节点为空,返回null,表示未找到最近公共祖先if (root == null)return null;// 如果当前节点是p或q,说明当前节点就是其中一个目标节点的最近公共祖先if (root == p || root == q)return root;// 递归查找左子树中的最近公共祖先TreeNode leftValue = lowestCommonAncestor(root.left, p, q);// 递归查找右子树中的最近公共祖先TreeNode rightValue = lowestCommonAncestor(root.right, p, q);// 左右子树分别找到了p和q的最近公共祖先if (leftValue != null && rightValue != null)return root; // 当前节点即为最近公共祖先// 只有右子树找到了最近公共祖先if (leftValue == null && rightValue != null) {return rightValue;} else if (leftValue != null && rightValue == null) { // 只有左子树找到了最近公共祖先return leftValue;} else {return null; // 左右子树均未找到最近公共祖先}
}
  • 如果当前节点 root 为空,直接返回 null,表示未找到最近公共祖先。
  • 如果当前节点 root 等于 p 或者 q,说明当前节点就是其中一个目标节点,直接返回当前节点 root
  • 如果左子树找到了 p 和 q 的最近公共祖先 leftValue,右子树也找到了 p 和 q 的最近公共祖先 rightValue,那么当前节点 root 就是它们的最近公共祖先,直接返回 root
  • 如果只有左子树找到了最近公共祖先 leftValue,则返回 leftValue
  • 如果只有右子树找到了最近公共祖先 rightValue,则返回 rightValue
  • 如果左右子树都没有找到,则返回 null,表示在当前子树中不存在 p 和 q 的最近公共祖先。

五、二叉搜索树的最近公共祖先

题目: 

给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

示例 1:

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

示例 2:

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

思路:

利用二叉搜索树的性质,如果p和q的值小于根节点的值,则说明其最近公共祖先在左子树中,反之如果p和q的值大于根节点的值,则说明其最近公共祖先在右子树中,在遍历过程中如果节点的值大于p且小于p,说明该节点为最近公共祖先

代码:

//递归法
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root == null)return null;// 如果当前节点的值大于p和q的值,则它们的最近公共祖先一定在左子树中if (root.val > p.val && root.val > q.val) {TreeNode left = lowestCommonAncestor(root.left, p, q);if (left != null) {return left; // 如果左子树递归找到了最近公共祖先,直接返回}} // 如果当前节点的值小于p和q的值,则它们的最近公共祖先一定在右子树中else if (root.val < p.val && root.val < q.val) {TreeNode right = lowestCommonAncestor(root.right, p, q);if (right != null) {return right; // 如果右子树递归找到了最近公共祖先,直接返回}}// 如果当前节点的值介于p和q的值之间,或者当前节点就是p或q,则当前节点就是最近公共祖先return root;
}
  • 如果当前节点的值大于 p 和 q 的值,则说明 p 和 q 都在当前节点的左子树中,递归地在左子树中查找最近公共祖先。
  • 如果当前节点的值小于 p 和 q 的值,则说明 p 和 q 都在当前节点的右子树中,递归地在右子树中查找最近公共祖先。
//迭代法
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {// 迭代查找最近公共祖先while (root != null) {if (root.val < p.val && root.val < q.val) {// 如果根节点的值都小于p和q的值,说明p和q都在右子树root = root.right; // 移动到右子树继续查找} else if (root.val > p.val && root.val > q.val) {// 如果根节点的值都大于p和q的值,说明p和q都在左子树root = root.left; // 移动到左子树继续查找} else {// 如果根节点的值介于p和q的值之间,或者根节点就是p或q,返回根节点return root; // 找到最近公共祖先,返回}}// 如果未找到最近公共祖先,返回nullreturn null;
}
  • 利用 while 循环,只要当前节点 root 不为空,就进行迭代处理。
  • 在每一轮迭代中,根据 root 的值和 p, q的值的关系来决定向左子树或右子树移动:
    • 如果 root.val 小于 p.val 和 q.val,说明 p 和 q 都在 root 的右子树中,因此将 root 移动到右子树 root.right
    • 如果 root.val 大于 p.val 和 q.val,说明 p 和 q 都在 root 的左子树中,因此将 root 移动到左子树 root.left
    • 如果 root.val 介于 p.val 和 q.val 之间,或者等于其中之一的值,说明 root 就是 p 和 q 的最近公共祖先,直接返回 root。

今天的学习就到这里 

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

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

相关文章

IE11添加收藏、关闭窗口时弹出的对话框字体又大又粗很难看的解决办法

原因已查明&#xff0c;在win7 sp1 32位系统下&#xff0c;安装“2020-01 适用于基于 x86 的系统的 Windows 7 月度安全质量汇总&#xff08;KB4534310&#xff09;”这个更新会导致IE11的窗口字体变大变粗&#xff0c;把这个更新卸载了就可以了&#xff0c;无需重装IE11浏览器…

【四】jdk8基于m2芯片arm架构Ubuntu24虚拟机下载与安装

文章目录 1. 安装版本2. 开始安装3. 集群安装 1. 安装版本 如无特别说明&#xff0c;本文均在root权限下安装。进入oracle官网&#xff1a;https://www.oracle.com/java/technologies/downloads/找到最下面Java SE 看到java 8&#xff0c;下载使用 ARM64 Compressed Archive版…

探索 Electron:快捷键与剪切板操作

Electron是一个开源的桌面应用程序开发框架&#xff0c;它允许开发者使用Web技术&#xff08;如 HTML、CSS 和 JavaScript&#xff09;构建跨平台的桌面应用程序&#xff0c;它的出现极大地简化了桌面应用程序的开发流程&#xff0c;让更多的开发者能够利用已有的 Web 开发技能…

C++:类和对象2

1.类的默认成员函数 默认成员函数就是用户没有显示实现编译器会自动生成的成员函数称为默认成员函数。一个类&#xff0c;我们在不写的情况下编译器会默认生成6个默认成员函数&#xff0c;分别是构造函数&#xff0c;析构函数&#xff0c;拷贝构造函数&#xff0c;拷贝赋值运算…

GPT-4引领:AI新浪潮的转折点

OneFlow编译 **翻译&#xff5c;贾川、杨婷、徐佳渝 编辑&#xff5c;王金许** 一朝成名天下知。ChatGPT/GPT-4相关的新闻接二连三刷屏朋友圈&#xff0c;如今&#xff0c;这些模型背后的公司OpenAI的知名度不亚于任何科技巨头。 不过&#xff0c;就在ChatGPT问世前&#x…

Reaxys平台账号创建:简易注册流程

Reaxys数据库是Elsevier旗下的全球最大物质理化性质和事实反应数据库&#xff0c;包含了超过5亿条经过实验验证的物质信息&#xff0c;收录超过1.38亿种化合物&#xff0c;5,000万种单步和多步反应、6,000万条文摘记录。涵盖全球7大专利局和16,000种期刊16个学科中与化合物性质…

全网最详细Gradio教程系列5——Gradio Client: python

全网最详细Gradio教程系列5——Gradio Client: python 前言本篇摘要5. Gradio Client的三种使用方式5.1 使用Gradio Python Client5.1.1 安装gradio_client5.1.2 连接Gradio应用程序1. 通过URL连接2. 通过SpaceID连接3. 辅助&#xff1a;duplicate()和hf_token4. Colab Noteboo…

ajax学习1

<!-- 目标&#xff1a;使用axios库&#xff0c;获取省份列表数据&#xff0c;展示到页面上 1.引入axios库 --> <p class"my-p"></p> <script src"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></ script> <sc…

Tomcat项目本地部署

今天来分享一下如何于本机上在不适用idea等辅助工具的前提下&#xff0c;部署多个tomcat的web项目 我这里以我最近写的SSM项目哈米音乐为例&#xff0c;简单介绍一下项目的大致组成&#xff1a; 首先&#xff0c;项目分为4个模块&#xff0c;如下图所示&#xff1a; 其中&…

力扣高频SQL 50题(基础版)第十八题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题1633. 各赛事的用户注册率题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题 1633. 各赛事的用户注册率 题目说明 用户表&#xff1a; Users --…

RPG素材Unity7月20闪促限时4折游戏开发资产兽人角色模型动画休闲放置模板物理交互流体水下焦散VR界面UI2D模板场景20240720

今天这个是RPG素材比较多&#xff0c;还有一些休闲放置模板、FPS场景素材、角色模型、动画、特效。 详细内容展示&#xff1a;www.bilibili.com/video/BV1Tx4y1s7vm 闪促限时4折&#xff1a;https://prf.hn/l/0eEOG1P 半价促销&#xff1a;https://prf.hn/l/RlDmDeQ 7月闪促…

小红书(社招二面)算法原题

萝卜快跑涨价 距离我们上次谈 萝卜快跑 不足半月&#xff0c;萝卜快跑迎来了不少"反转"。 先是被曝远程后台有人操控&#xff0c;真实日成本超 400&#xff1a; 最近还被不少网友吐槽&#xff1a;萝卜快跑涨价了&#xff0c;如今价格和网约车持平。 据不少博主实测&a…

17 Python常用内置函数——基本输入输出

input() 和 print() 是 Python 的基本输入输出函数&#xff0c;前者用来接收用户的键盘输入&#xff0c;后者用来把数据以指定的格式输出到标准控制台或指定的文件对象。无论用户输入什么内容&#xff0c;input() 一律作为字符串对待&#xff0c;必要时可以使用内置函数 int()、…

【SpringBoot教程:从入门到精通】掌握Springboot开发技巧和窍门(四)-Vue项目配置环境、导航栏

主要写前端页面&#xff0c;采用vue框架写页面的导航栏&#xff01;&#xff01;&#xff01; 文章目录 前言 Vue项目配置环境 安装依赖 创建菜单 总结 前言 主要写前端页面&#xff0c;采用vue框架写页面的导航栏&#xff01;&#xff01;&#xff01; Vue项目配置环境 安装…

【算法】分布式共识Paxos

一、引言 在分布式系统中&#xff0c;一致性是至关重要的一个问题。Paxos算法是由莱斯利兰伯特&#xff08;Leslie Lamport&#xff09;在1990年提出的一种解决分布式系统中一致性问题的算法。 二、算法原理 Paxos算法的目标是让一个分布式系统中的多个节点就某个值达成一致。算…

2000-2023年上市公司全要素生产率数据LP法(含原始数据+计算代码+结果)

2000-2023年上市公司全要素生产率数据LP法&#xff08;含原始数据计算代码结果&#xff09; 1、时间&#xff1a;2000-2023年 2、指标&#xff1a;stkcd、year、证券代码、固定资产净额、资产总计、负债合计、支付给职工以及为职工支付的现金、购建固定资产无形资产和其他长期…

Monaco 使用 LinkedEditingRangeProvider

Monaco LinkEdit 功能是指同时修改同样的字符串&#xff0c;例如在编辑 Html 时&#xff0c;修改开始标签时会同时修改闭合标签。Monaco 支持自定义需要一起更新的字符串列表。最终效果如下&#xff1a; 首先&#xff0c;通过 registerLinkedEditingRangeProvider 注册 LinkEd…

关键词查找【Knuth-Morris-Pratt (KMP) 算法】

一个视频让你彻底学懂KMP算法_哔哩哔哩_bilibili KMP算法的核心是利用匹配失败后的信息&#xff0c;尽量减少模式串与主串的匹配次数以达到快速匹配的目的。 第一步&#xff1a;计算模式串(子串)和next[j]数组 模式串 前2位字母的next[j]固定是0 和 1 后续字母的nex[j]&…

生成式AI的发展方向是chat还是Agent探讨

生成式 AI 的发展方向&#xff0c;是 Chat 还是 Agent&#xff1f; 随着生成式AI技术的不断进步&#xff0c;关于其未来发展方向的讨论也愈发激烈。究竟生成式AI的未来是在对话系统&#xff08;Chat&#xff09;中展现智慧&#xff0c;还是在自主代理&#xff08;Agent&#x…

MySQL之触发器和存储过程

1、触发器 触发器简介 触发器&#xff08;trigger&#xff09;是一个特殊的存储过程&#xff0c;它的执行不是由程序调用&#xff0c;也不是手工启动&#xff0c;而是由事件来触 发&#xff0c;比如当对一个表进行操作&#xff08; insert&#xff0c;delete&#xff0c; upda…