450.删除二叉搜索树中的节点-medium

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

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

首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 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

思路:

  • 二叉搜索树,因为其是有序的,左边的节点都比根节点小,右边的节点都比根节点大;
  • 要删除某一个节点,分情况讨论:1. 如果是叶节点,则直接删除;
  • 2.如果是非叶子节点,且有左儿子节点,则找到其前驱节点(因为前驱节点是:在比当前节点小的节点中,最大的一个,将他放到当前节点位置,其左边的儿子节点还是都比他小,满足二叉搜索树定义);
  • 3.如果是非叶子节点,且没有左儿子,只有右儿子节点,则找到其后继节点(因为后继节点是:比当前节点大的节点中,最小的那一个,将他替换后,满足右边都比他大);
  • 将当前节点的值,换为前驱/后继节点的值,再递归删除这个前驱/后继节点即可。

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null) return null;
        if(key < root.val) root.left = deleteNode(root.left, key); //key在左边子节点
        else if(key > root.val) root.right = deleteNode(root.right, key); //key在右边子节点
        else{ // 当前节点就是需要删除的节点
            if(root.left == null && root.right == null) return null;//如果是叶子节点,直接删除即可
            else if(root.left != null){ //非叶子节点,如果有左儿子节点,则寻找其前驱节点
                int num = preNode(root);
                root.val = num; //将前驱节点的值,赋给当前节点,删掉这个前驱节点
                root.left = deleteNode(root.left, num);
            }else{ // 非叶子节点,如果没有左儿子,但有右儿子节点,寻找其后继节点
                int num = afterNode(root);
                root.val = num; //同理,删掉后继节点
                root.right = deleteNode(root.right, num);
            }
        }
        return root; //返回根节点
    }
    private int preNode(TreeNode root){ //寻找根节点的前驱节点
        root = root.left;
        while(root.right != null) root = root.right;
        return root.val;
    }

    private int afterNode(TreeNode root){ //寻找根节点的后继节点
        root = root.right;
        while(root.left != null) root = root.left;
        return root.val;
    }
}
原文地址:https://www.cnblogs.com/luo-c/p/13944400.html