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

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} key
 * @return {TreeNode}
 */
var deleteNode = function (root, key) {
    // 二叉搜索树特点
    // 1.左节点<父节点<右节点
    // 2.根节点大于左子树的所有节点
    // 3.根节点小于右子树的所有节点

    if (!root) {
        return null
    }
    if (root.val === key && root.left === null && root.right === null) { // 直接删除根节点
        return null
    }

    // 先找到要删除的节点
    let cur = root
    let parent = null // 父节点
    while (cur) {
        if (key < cur.val) {
            parent = cur
            cur = cur.left
        } else if (key > cur.val) {
            parent = cur
            cur = cur.right
        } else if (key === cur.val) { // 找到了
            // 有3种情况

            // 1.待删除节点为叶子节点:只需把父节点原来指向该节点的链指向null。
            if (cur.left === null && cur.right === null) {
                if (parent === null) { // parent为null,说明删除的是根节点
                    return null
                }
                if (cur.val < parent.val) {
                    parent.left = null
                } else {
                    parent.right = null
                }
                break
            }

            // 2.待删除节点的左节点或右节点为null:只需把父节点原来指向该节点的链指向剩下的左节点或右节点。
            if (cur.left === null) { // 左节点为null
                if (parent === null) { // parent为null,说明删除的是根节点
                    return cur.right // 直接返回右子树
                }
                if (cur.val < parent.val) {
                    parent.left = cur.right
                } else {
                    parent.right = cur.right
                }
                break
            }
            if (cur.right === null) { // 右节点为null
                if (parent === null) { // parent为null,说明删除的是根节点
                    return cur.left // 直接返回左子树
                }
                if (cur.val < parent.val) {
                    parent.left = cur.left
                } else {
                    parent.right = cur.left
                }
                break
            }

            // 3.待删除节点的左右节点都存在:
            // (1)找到该节点的左子树的最大值节点 或 右子树的最小值节点,下面以右子树最小值为例
            // (2)把最小值赋值给该节点
            // (3)删除最小值节点
            // (4)删除最小值节点后,右子树的根节点可能发生变化,当前节点要重新连接右子树的根节点
            if (cur.left !== null && cur.right !== null) {
                const minNode = getMinNode(cur.right) // 找到右子树最小值节点
                cur.val = minNode.val // 最小值先存起来
                cur.right = deleteNode(cur.right, minNode.val) // 删除最小值节点
                break
            }
        }
    }

    return root
};

/**
 * @param {TreeNode} root
 * @param {number} key
 * @return {TreeNode}
 */
var deleteNode = function (root, key) {
    if (!root) { // 递归结束条件
        return null
    }

    // 递归找到待删除的节点然后删除
    if (key < root.val) {
        // 往左子树找,删除节点后左子树的根节点可能发生变化,所以需要把当前节点连接新的左子树根节点
        root.left = deleteNode(root.left, key)
        return root
    } else if (key > root.val) {
        // 往右子树找,删除节点后右子树的根节点可能发生变化,所以需要重新连接新的右子树根节点
        root.right = deleteNode(root.right, key)
        return root
    } else if (key === root.val) { // 找到了
        // 3种情况
        // 第1种:待删除节点为叶子节点,只需把父节点指向该节点的链指向null
        // 第2种:待删除节点左节点或右节点为null,只需把父节点指向该节点的链指向剩下的左子树或右子树
        // 第3种:待删除节点的左右节点都存在
        // (1)找到待删除节点 左子树的最大值节点 或 右子树的最小值节点;以右子树最小值节点为例
        // (2)将最小值节点的val赋值给当前节点
        // (3)然后删除最小值节点(采用递归)
        // (4)删除最小值节点后,右子树根节点可能发生变化,当前节点应重新连接新的右子树根节点
        if (root.left === null && root.right === null) {
            return null
        } else if (root.left === null) {
            return root.right
        } else if (root.right === null) {
            return root.left
        } else if (root.left && root.right) {
            let minNode = getMinNode(root.right)
            root.val = minNode.val
            root.right = deleteNode(root.right, minNode.val)
            return root
        }
    }
}

/**
 * 获取最小值节点
 * @param {TreeNode} root 
 */
function getMinNode(root) {
    // 最小值在最左下角
    let cur = root
    while (cur.left !== null) {
        cur = cur.left
    }
    return cur
}