LeetCode Notes_#450_删除二叉搜索树中的节点

LeetCode Notes_#450_删除二叉搜索树中的节点

Contents

题目

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

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。
    说明: 要求算法时间复杂度为 O(h),h 为树的高度。

示例:

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]。

    5
   / \
  2   6
   \   \
    4   7

思路分析

BST的删除操作是BST数据结构最难的一个操作,更详细的讲解可以参考 《算法第四版》3.2.3.5小节
主要分为两步:
1. 找到待删除节点
不能把搜索和删除两步操作分离,因为删除节点后,还要把新的子树连接到父节点。这个操作只能通过递归的回溯来进行。

       if(key < root.val){
            //因为key节点在左子树当中,所以就缩小范围,在左子树当中删除key节点
            root.left = deleteNode(root.left, key);
            //递归调用结束,把删除后的子树连接到root上面
            return root;
        }
        else if(key > root.val){
            //同理,右子树的情况
            root.right = deleteNode(root.right, key);
            return root;
        }

2. 删除待删除节点
删除操作需要分情况考虑,有如下的三种情况:
1. 待删除节点没有子节点,可以直接将其删除

2. 待删除节点只有一个子节点,用它的子节点代替它

3. 待删除节点有两个子节点,则需要用待删除节点在中序遍历中的前驱或者后继代替它

  • 前驱节点一定是左子树的最大节点(也就是左子树最右下角的节点)
  • 后继节点一定是右子树的最小节点(也就是右子树最左下角的节点)

总结来说,BST在删除节点之前,中序遍历有序。删除节点之后,中序遍历应该还是有序的。

解答

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null) return null;
        if(key < root.val){
            //因为key节点在左子树当中,所以就缩小范围,在左子树当中删除key节点
            root.left = deleteNode(root.left, key);
            //递归调用结束,把删除后的子树连接到root上面
            return root;
        }
        else if(key > root.val){
            //同理,右子树的情况
            root.right = deleteNode(root.right, key);
            return root;
        }
        //key == root.val,即遇到的root刚好就是待删除的节点
        else{
            //左子树为null,用右子树替代key节点(左右子树同时为null,也正确)
            if(root.left == null) return root.right;
            ////右子树为null,用左子树替代key节点
            else if(root.right == null) return root.left;
            else{
                //后继节点其实就是右子树里的最小值
                TreeNode successor = min(root.right);
                //在右子树当中需要删除successor,因为successor要和key交换位置
                successor.right = deleteMin(root.right);
                //左子树没有变化
                successor.left = root.left;
                return successor;
            }

        }
    }

    //找到BST当中的最小节点,返回这个节点
    private TreeNode min(TreeNode node){
        if(node.left == null) return node;
        return min(node.left);
    }

    //删除BST当中的最小节点,返回删除之后的树的根节点
    private TreeNode deleteMin(TreeNode node){
        //找到最小节点,没有左孩子,用它的右子树代替它
        if(node.left == null) return node.right;
        node.left = deleteMin(node.left);
        return node;
    }
}

复杂度分析

时间复杂度:O(h)
空间复杂度:O(1)

posted @ 2020-08-14 16:25  Howfar's  阅读(144)  评论(0编辑  收藏  举报