450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
解题思路:
二叉搜索树的特点:中序遍历的序列是递增排序的序列。
这边定义两个函数:successor和predecessor,分别用于查找二叉搜索树当前节点按照中序遍历的后继和前驱节点(前提要求当前节点必须有右、左子树)。具体的函数实现看代码的注释,大概的解释是,后继节点是在右子树的最左侧,前驱节点是左子树的最右侧。
递归函数deleteNode,用于删除节点,删除分三种情况:
第一种:要删除的值正好是叶节点,直接删除叶节点即可
第二种:当前节点不是叶节点,并且有右子树,找当前节点的后继节点,并且递归删除后继节点
第三种情况:当前节点不是叶子节点,只有左子树,找当前节点的前驱节点,并且递归删除前驱节点
要删除的节点直接用它的后继或前驱节点的值替换,然后再递归删除它的后继或前驱节点,其实递归到最后,删除的是叶节点,具体看代码实现。
代码实现:
# 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 successor(self, root):
# 将当前节点指向它的右子树
root = root.right
# 按照中序二叉查找树的顺序,右子树的最左下节点是当前节点的下一个顺序节点
# 所以循环结束条件是当前节点是否还有左子树
while root.left:
root = root.left
return root.val
# 按照中序遍历的顺序,查找二叉搜索树当前节点对应的上一个节点
# 前提是当前节点必须有左子树,即保证查找成功
def predecessor(self, root):
# 将当前节点指向它的左子树
root = root.left
# 按照中序二叉查找树的顺序,左子树的最右下节点是当前节点的上一个顺序节点
# 所以循环结束条件是当前节点是否还有右子树
while root.right:
root = root.right
return root.val
def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
if not root:
return None
# 删除分三种情况
# 要删除的值在右子树中
if key > root.val:
root.right = self.deleteNode(root.right, key)
# 要删除的值在左子树中
elif key < root.val:
root.left = self.deleteNode(root.left, key)
# 要删除的值就是当前值
# 这时候也分三种情况
else:
# 第一种:要删除的值正好是叶节点,直接删除叶节点即可
if not (root.left or root.right):
# 直接将该节点置空
root = None
# 第二种:当前节点不是叶节点,并且有右子树
# 找当前节点的后继节点,并且递归删除后继节点
elif root.right:
# 将当前节点的值赋值为后继几点的值
root.val = self.successor(root)
# 递归删除右子树的后继节点
root.right = self.deleteNode(root.right, root.val)
# 第三种情况:当前节点不是叶子节点,只有左子树
# 找当前节点的前驱节点,并且递归删除前驱节点
else:
root.val = self.predecessor(root)
root.left = self.deleteNode(root.left, root.val)
return root
参考文献:
https://leetcode-cn.com/problems/delete-node-in-a-bst/solution/shan-chu-er-cha-sou-suo-shu-zhong-de-jie-dian-by-l/