返回

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

删除二叉搜索树中的节点

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

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点; 如果找到了,删除它。

思路

搜索树的节点删除要比节点增加复杂的多,有很多情况需要考虑

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了
  • 找到删除的节点
    • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
    • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
    • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
    • 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

第五种情况例:

这里再介绍一种通用的删除,普通二叉树的删除方式(没有使用搜索树的特性,遍历整棵树),用交换值的操作来删除目标节点。

代码中目标节点(要删除的节点)被操作了两次:

  • 第一次是和目标节点的右子树最左面节点交换。
  • 第二次直接被NULL覆盖了。

代码

Go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 通用的二叉树的删除方式
func deleteNode(root *TreeNode, key int) *TreeNode {
    if root == nil {
        return nil
    }
    if key < root.Val { // key < root 向左搜索
        root.Left = deleteNode(root.Left, key)
        return root
    }
    if key > root.Val { // key > root 向右搜索
        root.Right = deleteNode(root.Right, key)
        return root
    }
    // 找到目标节点
    if root.Right == nil { // 目标节点的右孩子为空
        return root.Left // 返回左孩子
    }
    if root.Left == nil { // 目标节点的左孩子为空
        return root.Right // 返回右孩子
    }
    // 目标节点的左右孩子都非空
    minNode := root.Right     // 保存目标节点的右子树到minNode中
    for minNode.Left != nil { // 找到目标节点的右子树的最左面节点
        // 保存目标节点的右子树的最左面节点到minNode中
        minNode = minNode.Left
    }
    root.Val = minNode.Val               // 目标节点的值改为其右子树的最左面节点的值
    root.Right = deleteNode1(root.Right) // 目标节点的右子树
    return root
}

// 删除目标节点的右子树的最左面节点
func deleteNode1(root *TreeNode) *TreeNode {
    if root.Left == nil { // 若传入节点的左孩子为空
        pRight := root.Right // 保存传入节点的右子树到pRight中
        root.Right = nil     // 删除传入节点的右子树
        return pRight        // 返回传入节点的右子树
    }
    // 若传入节点的左孩子不为空
    root.Left = deleteNode1(root.Left) // 向左搜索
    return root
}

GitHub

Built with Hugo
Theme Stack designed by Jimmy