235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: 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 为不同节点且均存在于给定的二叉搜索树中。
递归法(版本一)
class Solution:
def traversal(self, cur, p, q):
if cur is None:
return cur
# 中
if cur.val > p.val and cur.val > q.val: # 左
left = self.traversal(cur.left, p, q)
if left is not None:
return left
if cur.val < p.val and cur.val < q.val: # 右
right = self.traversal(cur.right, p, q)
if right is not None:
return right
return cur
def lowestCommonAncestor(self, root, p, q):
return self.traversal(root, p, q)
首先,定义了一个名为Solution
的类,其中包含两个方法:traversal
和lowestCommonAncestor
。
-
traversal
方法是一个递归函数,它接收三个参数:当前节点和两个需要找到最低公共祖先的节点。在每次递归调用中,我们首先检查当前节点是否为空。如果为空,则直接返回。然后,我们比较当前节点的值和两个目标节点的值。如果当前节点的值大于两个目标节点的值,那么我们知道最低公共祖先必然在当前节点的左子树中,因此我们递归地在左子树中查找。同样地,如果当前节点的值小于两个目标节点的值,那么我们知道最低公共祖先必然在当前节点的右子树中,因此我们递归地在右子树中查找。如果当前节点的值介于两个目标节点的值之间,那么我们知道当前节点就是最低公共祖先,因此我们返回当前节点。 -
lowestCommonAncestor
方法则是用于找出二叉搜索树中两个给定节点的最低公共祖先。它接收三个参数:一个二叉搜索树的根节点和两个需要找到最低公共祖先的节点。这个方法调用了traversal
方法来进行查找,并返回查找到的结果。
701.二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据保证,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。
提示:
- 给定的树上的节点数介于 0 和 10^4 之间
- 每个节点都有一个唯一整数值,取值范围从 0 到 10^8
- -10^8 <= val <= 10^8
- 新值和原始二叉搜索树中的任意节点值都不同
递归法(版本二)
class Solution:
def insertIntoBST(self, root, val):
if root is None:
return TreeNode(val)
parent = None
cur = root
while cur:
parent = cur
if val < cur.val:
cur = cur.left
else:
cur = cur.right
if val < parent.val:
parent.left = TreeNode(val)
else:
parent.right = TreeNode(val)
return root
这段代码的目标是在二叉搜索树中插入一个新的节点。下面是代码的详细步骤:
- 首先,检查根节点是否为空。如果为空,那么新的节点就会成为根节点。
- 如果根节点不为空,那么就会进入一个循环,直到找到合适的位置来插入新的节点。在这个循环中,代码会检查新值是否小于当前节点的值。
- 如果新值小于当前节点的值,那么代码会向左子树移动;否则,它会向右子树移动。在每一步中,都会更新父节点为当前节点。
- 当找到合适的位置(即当前节点为空)时,代码会检查新值是否小于父节点的值。如果是,那么新节点将被插入到父节点的左侧;否则,它将被插入到父节点的右侧。
- 最后,函数返回根节点。
这个函数假设给定的二叉树是一个有效的二叉搜索树,并且没有重复的值。
450.删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点; 如果找到了,删除它。 说明: 要求算法时间复杂度为 $O(h)$,h 为树的高度。
示例:
递归法(版本一)
class Solution:
def deleteNode(self, root, key):
if root is None:
return root
if root.val == key:
if root.left is None and root.right is None:
return None
elif root.left is None:
return root.right
elif root.right is None:
return root.left
else:
cur = root.right
while cur.left is not None:
cur = cur.left
cur.left = root.left
return root.right
if root.val > key:
root.left = self.deleteNode(root.left, key)
if root.val < key:
root.right = self.deleteNode(root.right, key)
return root
这段代码的目标是删除二叉搜索树中的一个节点。二叉搜索树是一种特殊的树,其中每个节点的左子节点的值都小于该节点的值,右子节点的值都大于该节点的值。
- 首先,我们检查根节点是否为空,如果为空,那么我们没有什么可以删除的,所以直接返回空。
- 然后,我们检查根节点的值是否等于我们要删除的值。如果是,那么我们需要删除这个节点。这里有几种情况:
- 如果这个节点没有子节点(即它是一个叶子节点),那么我们可以直接删除它,返回None。
- 如果这个节点只有一个子节点,那么我们可以通过返回它的子节点来替换它。
- 如果这个节点有两个子节点,那么事情就变得复杂了。我们需要找到右子树中的最小值(也就是右子树中最左边的节点),然后用这个最小值来替换要删除的节点。
- 如果根节点的值大于我们要删除的值,那么我们知道要删除的节点在左子树中,所以我们对左子树递归调用删除函数。
- 如果根节点的值小于我们要删除的值,那么我们知道要删除的节点在右子树中,所以我们对右子树递归调用删除函数。
- 最后,无论发生什么情况,我们都返回根节点。