题目链接:leetcode450
分析
最基本的类型吧,但是实现起来考虑的还是比较烦
整理一下也方便后面的查阅
二叉搜索树的性质就不提了,就是中序遍历是有序的。
查找部分,就是一个简单的迭代,判断当前的root和我们的实际值大小关系,如果相同则直接跳出(我们把这个点叫做root)
找到之后,我们需要对之前的树进行调整,这里我们不需要进行删除操作,只需要修改原树的指向问题即可,剩下的垃圾回收会帮我们搞定。
这里的重点是调整的方式,对于一般的树来说(指那种结点比较多的,可以想成满二叉树?先考虑一般情况么)
我们可以将左子树的最后一个结点或者右子树的第一个结点作为新root
这块,替换的过程中有一个很烦的问题就是我们要修改root和替换结点的同时,还需要修改他们的父节点,那么就需要在遍历的同时记录下来。
接下来就是一些特殊情况的问题,比如我们的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]:
# 先找到再说吧
'''这里的father是root的父节点,old_root是方便我们return用的,branch记录father是通过left还是right到的root
他这里有一个很骚的逻辑,就是如果我删除的刚好是树中唯一的结点,那么此时树为空,所以我选择给原树直接找了个爹
这样防止一些边界条件的问题'''
father, branch = TreeNode(left=root), True
old_root = father
while root and root.val != key:
father = root
if root.val > key:
root = root.left
branch = True
else:
root = root.right
branch = False
if root:
# 我们要删除当前的这个位置
if not (root.left or root.right):
# 叶子节点,直接删除
if branch:
father.left = None
else:
father.right = None
elif root.left:
# 存在左子树,我们直接拉最后一个结点
'''下面的if分支是对root.left就是last_left的一个补丁,此时我们没法搞last_left_father'''
# 对于没有右孙子的,我们直接搞
if not root.left.right:
# last_left挪到root
last_left = root.left
last_left.right = root.right
else:
last_left = root.left
last_left_father = root
while last_left.right:
last_left_father = last_left
last_left = last_left.right
# 调整最后一个点的左子树
last_left_father.right = last_left.left
# last_left移动到root
last_left.left = root.left
last_left.right = root.right
# 调整father
if branch:
father.left = last_left
else:
father.right = last_left
else:
# 只有右子树,直接拉上去
if branch:
father.left = root.right
else:
father.right = root.right
return old_root.left
然后补一个递归的,是仿照leetcode官方解答写的,有两点比较新颖,标了一下
'''递归版本'''
if not root:
return None
else:
if root.val > key:
root.left = self.deleteNode(root.left, key)
elif root.val < key:
root.right = self.deleteNode(root.right, key)
else:
# 相同了,我们整理子树
# 此时是要用一个结点替代我们的根节点
if not root.left:
return root.right
elif not root.right:
return root.left
else:
# 我们这次找右子树的第一个结点
p = root.right
while p.left:
p = p.left
'''他这个函数调用是真的牛,因为我们的目的就是在root的右子树中删除p,
然后将其接到p的右子树上'''
p.right = self.deleteNode(root.right, p.val)
p.left = root.left
# 因为做了替换,所以这里是p,剩下的是root
return p
return root