一、235. 二叉搜索树的最近公共祖先
相对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。题目链接/文章讲解:代码随想录
1. 找最近祖先
(1)根据二叉搜索树的特性,中间节点一定大于其左子树所有值,一定小于右子树所有值。所以首先判断中间节点和p、q二者之间的大小,如果均大于中间节点,说明一定都在其右子树;反之,则都在左子树;如果一大一小,则在左右子树上。
(2)如何判断是最近的祖先而不是次近的:如果继续向下遍历,p或q有一个消失的话,说明错过p或q,不能继续向下遍历了,当前的中间节点就是最近的祖先。
2. 代码实现
(1)定义递归函数,传入的是当前遍历的节点、p和q,返回值是节点。
(2)确定终止条件:首先本题不需要前中后序,只需要左和右即可,中间节点无需处理。
1)如果当前节点的值大于p的值且大于q的值,说明p和q均在左子树上,所以向左去遍历搜索。终止条件就是当左为空,就返回节点。
2)如果当前节点的值小于p的值且小于q的值,说明p和q均在右子树上,所以向右去遍历搜索。终止条件就是当右为空,就返回节点。
(3)剩下的情况就是节点在p和q之间,不需要去管谁大谁小,直接返回当前节点即可。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root is None or root == p or root == q:
return root
# left:
if root.val > p.val and root.val > q.val:
left = self.lowestCommonAncestor(root.left, p, q)
if left is not None:
return left
# right:
if root.val < p.val and root.val < q.val:
right = self.lowestCommonAncestor(root.right, p, q)
if right is not None:
return right
return root
二、701.二叉搜索树中的插入操作
本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。题目链接/文章讲解:代码随想录
1. 解题思路
根据探究可以发现,任何一个要插入的节点都可以最终在某个叶子节点后插入,无需改变这颗二叉树的原始结构,这种做法是最简单的。
注意:如果问题将二叉搜索树变成平衡二叉树,那么超过一定深度就需要改变树的结构。
(1)确定递归函数的参数和返回值:递归函数的参数为传入的二叉树根节点和需要插入节点的值,返回值是改变后的二叉树根节点。
(2)确定终止条件:
递归遇到空,就终止递归,同时也找到了我们需要插入新节点的位置;所以定义一个新节点,传入数值,然后将这个新节点返回给上一个节点。
(3)如果当前节点的值大于新节点的值,那么向左去遍历,最后用root.left接住这个节点。
(4)如果当前节点的值小于新节点的值,那么向右去遍历,最后用root.right接住这个节点。
(5)最后返回root即可。
2. 代码实现
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if root is None:
node = TreeNode(val)
return node
if root.val > val:
root.left = self.insertIntoBST(root.left, val)
if root.val < val:
root.right = self.insertIntoBST(root.right, val)
return root
三、450.删除二叉搜索树中的节点
相对于 插入操作,本题就有难度了,涉及到改树的结构题目链接/文章讲解:代码随想录
1. 分析五种情况
所需删除的节点位置一共分以下五种情况:
(1)左子树和右子树均为空:说明需要删除的是叶子节点
(2)左子树为空和右子树不为空:直接将右子树的父节点改成待删除的节点的父节点
(3)右子树为空和左子树不为空:直接将左子树的父节点改成待删除的节点的父节点
(4)左子树和右子树均不为空:根据二叉搜索树的特性,将左子树的最大值(即左子树的根节点)连接到右子树的最小值(即右子树最左侧的叶子节点)下面。
(5)没找到需要删除的根节点。
2. 代码实现
(1)定义递归函数的参数和返回值:传入参数二叉搜索树根节点,以及需要删除的节点值,返回的是更新后的二叉树根节点。
(2)确定终止条件:即找到需要删除的节点值就终止递归,进行删除操作。
1)没有找到待删除节点/该二叉树为空:返回null。
2)找到待删除节点:
A. 如果待删除节点为叶子节点:即左右子树均为空,那么进行删除节点操作,直接返回null,也就是将null返回给了叶子节点的父节点。
B. 如果待删除节点左不为空而右为空:返回左子树,也就是将左子树返回给了待删除节点的父节点。
C. 如果待删除节点左为空而右不为空:返回左子树,也就是将右子树返回给了待删除节点的父节点。
D. 如果待删除节点左右均不为空:即需要将左子树的根节点移动到右子树的最左侧的叶子节点下面。a. 首先定义cur指针指向待删除节点,然后对右子树一直进行向左遍历,直到来到其最左侧的叶子节点; b.然后将待删除节点的左子树移动到该叶子节点;c.删除待删除节点:让待删除节点的父节点直接指向右子树的根节点即可,在此处我们直接返回右子树给父节点。
(3)单层递归逻辑:
1)根据二叉搜索树的特性,如果root.val大于key,那么直接向左递归,注意递归函数有返回值,所以需要root.left接住;
2)根据二叉搜索树的特性,如果root.val小于key,那么直接向右递归,注意递归函数有返回值,所以需要root.right接住;
(4)最后返回root。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
# 确定递归的终止条件——找到待删除节点:
if root is None:
return None
if root.val == key:
# 待删除节点是叶子节点:
if root.left is None and root.right is None:
return None
elif root.left is not None and root.right is None:
return root.left
elif root.left is None and root.right is not None:
return root.right
else: # 待删除节点左右子树均不为空
cur = root.right
while cur.left:
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