代码随想录-Day22

2024-05-29 23:20
文章标签 随想录 代码 day22

本文主要是介绍代码随想录-Day22,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

235. 二叉搜索树的最近公共祖先

方法一:两次遍历

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {List<TreeNode> path_p = getPath(root, p);List<TreeNode> path_q = getPath(root, q);TreeNode ancestor = null;for (int i = 0; i < path_p.size() && i < path_q.size(); ++i) {if (path_p.get(i) == path_q.get(i)) {ancestor = path_p.get(i);} else {break;}}return ancestor;}public List<TreeNode> getPath(TreeNode root, TreeNode target) {List<TreeNode> path = new ArrayList<TreeNode>();TreeNode node = root;while (node != target) {path.add(node);if (target.val < node.val) {node = node.left;} else {node = node.right;}}path.add(node);return path;}
}

这段代码定义了一个名为 Solution 的类,用于求解二叉搜索树(Binary Search Tree, BST)中两个指定节点的最近公共祖先(Lowest Common Ancestor, LCA)。类中包含两个方法:

  1. lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)

    • 功能:该方法接收BST的根节点 root 和需要寻找LCA的两个节点 pq 作为参数,返回这两个节点的最近公共祖先。
    • 实现:首先,分别调用 getPath 方法获取节点 pq 到根节点的路径(以节点列表形式存储)。然后,遍历这两个路径,找到路径中最后一个相同的节点,即为最近公共祖先。如果两个路径没有共同节点(理论上在BST中不会发生,除非 pq 不在树中),则返回 null(虽然代码中未直接处理这种情况,但循环结束时 ancestor 仍为 null)。
  2. getPath(TreeNode root, TreeNode target)

    • 功能:该方法接收BST的根节点 root 和目标节点 target,返回从根到目标节点的路径上的所有节点列表。
    • 实现:通过一个循环不断比较当前节点与目标节点的值,决定是向左子树还是右子树移动。同时,将访问过的节点按路径顺序添加到列表 path 中。当找到目标节点时,将目标节点也添加到路径列表,并返回整个路径。

注意,这段代码的高效运行依赖于输入是二叉搜索树的特性,即树中任意节点的左子树所有节点的值都小于该节点的值,右子树所有节点的值都大于该节点的值。这使得在寻找特定值的节点时,可以快速地在树中进行定向移动,而不需要回溯,因此每个 getPath 方法的时间复杂度为O(log n),其中n是树中的节点数。整体的 lowestCommonAncestor 方法的时间复杂度也是O(log n),因为两个路径的最长公共前缀长度不会超过树的高度。

方法二:一次遍历

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {TreeNode ancestor = root;while (true) {if (p.val < ancestor.val && q.val < ancestor.val) {ancestor = ancestor.left;} else if (p.val > ancestor.val && q.val > ancestor.val) {ancestor = ancestor.right;} else {break;}}return ancestor;}
}

这段代码定义了一个名为 Solution 的类,其中包含一个方法 lowestCommonAncestor,用于在给定的二叉搜索树(BST)中找到两个指定节点 pq 的最近公共祖先(LCA)。这个方法直接利用了BST的性质(即左子树所有节点的值小于根节点值,右子树所有节点的值大于根节点值),以迭代而非递归的方式高效地解决了问题。以下是代码的详细解释:

  • 方法签名public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) 接受三个参数,分别是BST的根节点 root,以及需要找LCA的两个节点 pq

  • 初始化祖先节点:首先将 ancestor 初始化为根节点 root

  • 循环查找:进入一个 while 循环,循环条件默认为 true,意味着它将一直执行,直到通过 break 语句跳出循环。

    • 在循环内,首先检查 pq 的值与当前 ancestor 节点值的关系:
      • 如果 pq 的值都小于 ancestor 的值,说明它们都在当前节点的左子树中,因此将 ancestor 更新为其左子节点。
      • 如果 pq 的值都大于 ancestor 的值,说明它们都在当前节点的右子树中,因此将 ancestor 更新为其右子节点。
      • 如果以上两种情况都不满足,说明当前 ancestor 节点满足以下至少一种情况:
        • 它正好是 pq 中的一个。
        • 它位于 pq 之间,即一个在它的左子树,另一个在它的右子树,或者两者都在同一边但当前节点就是最近的共同祖先。
      • 在这种情况下,通过 break 退出循环,因为已经找到了最近公共祖先。
  • 返回结果:循环结束后,ancestor 节点即为所求的最近公共祖先,直接返回该节点。

这种方法的时间复杂度为 O(h),其中 h 是树的高度,因为在最坏情况下,我们可能需要从根节点一直走到 pq 中较深的那个节点。由于是二叉搜索树,高度 h 最坏情况下为 n(树退化为链表的情况),最好情况下为 log(n)。空间复杂度为 O(1),因为我们只使用了固定数量的指针变量,没有使用额外的空间来存储节点路径等。

701. 二叉搜索树中的插入操作

class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {if (root == null) {return new TreeNode(val);}TreeNode pos = root;while (pos != null) {if (val < pos.val) {if (pos.left == null) {pos.left = new TreeNode(val);break;} else {pos = pos.left;}} else {if (pos.right == null) {pos.right = new TreeNode(val);break;} else {pos = pos.right;}}}return root;}
}

这段代码定义了一个名为 Solution 的类,其中包含一个方法 insertIntoBST,该方法用于将一个值插入到给定的二叉搜索树(Binary Search Tree, BST)中。二叉搜索树的特性是:对于任意一个节点,其左子树中的所有节点的值都小于该节点的值,其右子树中的所有节点的值都大于该节点的值。以下是方法的逻辑解析:

  • 方法签名public TreeNode insertIntoBST(TreeNode root, int val) 接受两个参数,一个是BST的根节点 root,另一个是要插入的值 val

  • 基本情况处理:首先检查 root 是否为空。如果为空,说明当前树为空,那么直接创建一个新的 TreeNode,其值为 val,并返回这个新节点作为新树的根。

  • 寻找插入位置:如果根节点不为空,声明一个指针 pos 指向当前考虑的节点(初始化为根节点 root),然后进入一个 while 循环,用于找到插入新节点的位置。

    • 在循环中,如果 val 小于 pos 节点的值,则向左子树移动;如果 val 大于或等于 pos 节点的值,则向右子树移动。每次移动时,如果遇到空的子节点(即找到了插入位置),则在相应位置创建一个新节点,值为 val,并跳出循环。
  • 返回结果:循环结束后,无论是否插入新节点,根节点 root 的引用都保持不变,直接返回 root 即可。因为插入操作是在原有的树结构基础上进行的,没有改变根节点本身,只是在其某个子树上新增了节点。

这种方法保持了二叉搜索树的性质,插入操作的时间复杂度在最坏情况下为 O(H),其中 H 是树的高度。对于平衡的二叉搜索树,平均情况下插入操作的时间复杂度为 O(log N),N 为树中节点的数量。

450. 删除二叉搜索树中的节点

方法一:递归

class Solution {public TreeNode deleteNode(TreeNode root, int key) {if (root == null) {return null;}if (root.val > key) {root.left = deleteNode(root.left, key);return root;}if (root.val < key) {root.right = deleteNode(root.right, key);return root;}if (root.val == key) {if (root.left == null && root.right == null) {return null;}if (root.right == null) {return root.left;}if (root.left == null) {return root.right;}TreeNode successor = root.right;while (successor.left != null) {successor = successor.left;}root.right = deleteNode(root.right, successor.val);successor.right = root.right;successor.left = root.left;return successor;}return root;}
}

这段代码定义了一个名为 Solution 的类,其中包含一个方法 deleteNode,用于在给定的二叉搜索树(BST)中删除具有给定值的节点。二叉搜索树的特性是左子树中的所有节点的值小于当前节点值,右子树中的所有节点的值大于当前节点值。下面是代码逻辑的详细解析:

  1. 基本情况处理:首先检查根节点是否为空,如果为空,则直接返回 null,表示树为空或目标节点不存在。

  2. 查找目标节点:利用BST的性质进行查找。如果目标值 key 小于当前节点值 root.val,则在左子树中递归删除;如果 key 大于当前节点值,则在右子树中递归删除。这一步骤会一直进行到找到目标节点或递归到空节点为止。

  3. 删除目标节点

    • 当找到目标节点(即 root.val == key)时,有三种情况:
      • 叶子节点:如果目标节点没有左右子节点,直接返回 null,让其父节点指向 null 达到删除效果。
      • 仅有一个子节点:如果目标节点只有左子节点或右子节点,直接返回其非空的子节点,使父节点指向这个子节点。
      • 有两个子节点:找到目标节点的后继节点(即右子树中的最小节点 successor),用后继节点替换当前目标节点。然后在右子树中递归删除这个后继节点(因为后继节点可能是其所在子树的最小值,也可能有右子节点,故需要递归删除以保持BST性质)。后继节点的左子树挂载到原目标节点的左子树上,原目标节点的右子树挂载到后继节点的右子树上,这样既删除了目标节点,又保持了BST的结构。
  4. 返回处理结果:在递归的每一步中,直接返回处理后的子树根节点,这样可以保证上一层递归能够正确接收到更新后的子树状态。

通过上述逻辑,该方法能够有效地在二叉搜索树中删除指定值的节点,同时保持树的二叉搜索特性。

这篇关于代码随想录-Day22的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1014921

相关文章

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

使用Spring Cache本地缓存示例代码

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac... 目录一、Spring Cache简介核心特点:二、基础配置1. 添加依赖2. 启用缓存3. 缓存配置方案方案

MySQL的配置文件详解及实例代码

《MySQL的配置文件详解及实例代码》MySQL的配置文件是服务器运行的重要组成部分,用于设置服务器操作的各种参数,下面:本文主要介绍MySQL配置文件的相关资料,文中通过代码介绍的非常详细,需要... 目录前言一、配置文件结构1.[mysqld]2.[client]3.[mysql]4.[mysqldum

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

IDEA与MyEclipse代码量统计方式

《IDEA与MyEclipse代码量统计方式》文章介绍在项目中不安装第三方工具统计代码行数的方法,分别说明MyEclipse通过正则搜索(排除空行和注释)及IDEA使用Statistic插件或调整搜索... 目录项目场景MyEclipse代码量统计IDEA代码量统计总结项目场景在项目中,有时候我们需要统计

MySQL设置密码复杂度策略的完整步骤(附代码示例)

《MySQL设置密码复杂度策略的完整步骤(附代码示例)》MySQL密码策略还可能包括密码复杂度的检查,如是否要求密码包含大写字母、小写字母、数字和特殊字符等,:本文主要介绍MySQL设置密码复杂度... 目录前言1. 使用 validate_password 插件1.1 启用 validate_passwo