zoukankan      html  css  js  c++  java
  • [LeetCode] 450. Delete Node in a BST 删除二叉搜索树中的节点

    Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST.

    Basically, the deletion can be divided into two stages:

    1. Search for a node to remove.
    2. If the node is found, delete the node.

    Note: Time complexity should be O(height of tree).

    Example:

    root = [5,3,6,2,4,null,7]
    key = 3
    
        5
       / 
      3   6
     /    
    2   4   7
    
    Given key to delete is 3. So we find the node with value 3 and delete it.
    
    One valid answer is [5,4,6,2,null,null,7], shown in the following BST.
    
        5
       / 
      4   6
     /     
    2       7
    
    Another valid answer is [5,2,6,null,4,null,7].
    
        5
       / 
      2   6
          
        4   7
    

    这道题让我们删除二叉搜索树中的一个节点,难点在于删除完结点并补上那个结点的位置后还应该是一棵二叉搜索树。被删除掉的结点位置,不一定是由其的左右子结点补上,比如下面这棵树:

             7
            /
           4   8
         /      
        2     6
            /
          3 5

    如果要删除结点4,那么应该将结点5补到4的位置,这样才能保证还是 BST,那么结果是如下这棵树:

             7
            /
           5   8
         /      
        2     6
            
          3

    先来看一种递归的解法,首先判断根节点是否为空。由于 BST 的左<根<右的性质,使得可以快速定位到要删除的结点,对于当前结点值不等于 key 的情况,根据大小关系对其左右子结点分别调用递归函数。若当前结点就是要删除的结点,先判断若有一个子结点不存在,就将 root 指向另一个结点,如果左右子结点都不存在,那么 root 就赋值为空了,也正确。难点就在于处理左右子结点都存在的情况,需要在右子树找到最小值,即右子树中最左下方的结点,然后将该最小值赋值给 root,然后再在右子树中调用递归函数来删除这个值最小的结点,参见代码如下:

    解法一:

    class Solution {
    public:
        TreeNode* deleteNode(TreeNode* root, int key) {
            if (!root) return NULL;
            if (root->val > key) {
                root->left = deleteNode(root->left, key);
            } else if (root->val < key) {
                root->right = deleteNode(root->right, key);
            } else {
                if (!root->left || !root->right) {
                    root = (root->left) ? root->left : root->right;
                } else {
                    TreeNode *cur = root->right;
                    while (cur->left) cur = cur->left;
                    root->val = cur->val;
                    root->right = deleteNode(root->right, cur->val);
                }
            }
            return root;
        }
    };

    下面来看迭代的写法,还是通过 BST 的性质来快速定位要删除的结点,如果没找到直接返回空。遍历的过程要记录上一个位置的结点 pre,如果 pre 不存在,说明要删除的是根结点,如果要删除的结点在 pre 的左子树中,那么 pre 的左子结点连上删除后的结点,反之 pre 的右子结点连上删除后的结点。在删除函数中,首先判空,若为空,直接返回空指针;否则检测若右子结点不存在,直接返回左子结点即可,因为没有右子树就不会牵扯到调整树结构的问题;若右子结点存在,需要找到右子树中的最小值,即右子树中的最左子结点,用一个 while 循环找到即可,然后将要删除结点的左子结点连到右子树的最左子结点的左子结点上即可(说的有点绕,大家仔细体会一下),最后返回要删除结点的右子结点即可,文字表述确实比较绕,请大家自行带例子一步一步观察就会很清晰明了,参见代码如下:

    解法二:

    class Solution {
    public:
        TreeNode* deleteNode(TreeNode* root, int key) {
            if (!root) return nullptr;
            TreeNode *cur = root, *pre = nullptr;
            while (cur) {
                if (cur->val == key) break;
                pre = cur;
                if (cur->val > key) cur = cur->left;
                else cur = cur->right;
            }
            if (!pre) return del(cur);
            if (pre->left && pre->left->val == key) pre->left = del(cur);
            else pre->right = del(cur);
            return root;
        }
        TreeNode* del(TreeNode* node) {
            if (!node) return nullptr;
            if (!node->right) return node->left;
            TreeNode *t = node->right;
            while (t->left) t = t->left;
            t->left = node->left;
            return node->right;
        }
    };

    下面来看一种对于二叉树通用的解法,适用于所有二叉树,所以并没有利用 BST 的性质,而是遍历了所有的结点,然后删掉和 key 值相同的结点,参见代码如下:

    解法三:

    class Solution {
    public:
        TreeNode* deleteNode(TreeNode* root, int key) {
            if (!root) return NULL;
            if (root->val == key) {
                if (!root->right) return root->left;
                else {
                    TreeNode *cur = root->right;
                    while (cur->left) cur = cur->left;
                    swap(root->val, cur->val);
                }
            }
            root->left = deleteNode(root->left, key);
            root->right = deleteNode(root->right, key);
            return root;
        }
    };

    Github 同步地址:

    https://github.com/grandyang/leetcode/issues/450

    类似题目:

    Split BST

    参考资料:

    https://leetcode.com/problems/delete-node-in-a-bst/

    https://leetcode.com/problems/delete-node-in-a-bst/discuss/93296/Recursive-Easy-to-Understand-Java-Solution

    https://leetcode.com/problems/delete-node-in-a-bst/discuss/93378/An-easy-understanding-O(h)-time-O(1)-space-Java-solution.

    https://leetcode.com/problems/delete-node-in-a-bst/discuss/93331/concise-c-iterative-solution-and-recursive-solution-with-explanations

    https://leetcode.com/problems/delete-node-in-a-bst/discuss/93293/Very-Concise-C%2B%2B-Solution-for-General-Binary-Tree-not-only-BST

    LeetCode All in One 题目讲解汇总(持续更新中...)

  • 相关阅读:
    纪念这一天,我找到了实习工作
    在编程的道路上坚定地走下去
    今天是1024程序员节
    趣谈函数调用与返回值
    为期3个月的实训结束了,有感而发
    学习编程时遇到难点怎么办?
    今天学习了安卓中的UI线程
    Java程序员要掌握的常用快捷键
    我是一个注重基础的人
    我也有自己的小家啦
  • 原文地址:https://www.cnblogs.com/grandyang/p/6228252.html
Copyright © 2011-2022 走看看