zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    WorkFlow
    自己写的一个多线程的consumer 和 producter 模式
    Visual Studio进行Web性能测试
    基元线程同步——内核模式构造
    系统架构师
    《构建高性能的web站点》读书笔记缓存
    python中的代码对象
    python web框架互相融合, Pyramid或取代Django
    海量数据处理专题
    Django框架学习Forms篇
  • 原文地址:https://www.cnblogs.com/Howfars/p/13502927.html
Copyright © 2011-2022 走看看