删除二叉搜索树中的节点(力扣450)



题目

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

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

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

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。

在这里插入图片描述

前知

二叉搜索树是什么?

二叉搜索树(Binary Search Tree,简称BST)是一种常用的二叉树结构,它具有以下性质:

  1. 左子树的节点值小于根节点的值:每个节点都有一个左子树,且左子树中的所有节点的值都小于该节点的值。
  2. 右子树的节点值大于根节点的值:每个节点都有一个右子树,且右子树中的所有节点的值都大于该节点的值。
  3. 左右子树也是二叉搜索树:左子树和右子树也分别满足以上两个性质,即它们也是二叉搜索树。

由于二叉搜索树的这些性质,它具有很多有用的特性,其中最重要的是可以在平均O(log n)的时间复杂度内进行搜索、插入和删除操作。这是因为在一个平衡的二叉搜索树中,每次操作都会将搜索范围减半,从而实现了快速的查找。
二叉搜索树常被用于实现集合、映射等数据结构,也被广泛用于算法设计和实现中。然而,需要注意的是,如果二叉搜索树不平衡,即树的高度过高,搜索、插入和删除操作的时间复杂度可能会退化为O(n),这时可能需要进行平衡操作来维持其性能。


题解

一、思路

在这个题目中,我们需要实现一个函数来删除二叉搜索树中的指定节点,并且保持二叉搜索树的性质。想要删除二叉搜索树中的结点比增加结点更有难度,增加只需要找到叶子结点,在尾部添加即可,不用改变树的结构

而删除结点分为5种情况,第一种是没有找到删除的结点,遍历到空结点直接返回空;第二种情况是找到删除的结点,并且是叶子结点;第三种情况是找到删除的结点,当前结点为左空右不空的情况;第四种情况是找到删除的结点,当前结点为右空左不空的情况;第五种情况是找到删除的结点,当前结点为左右都不空的情况。然后递归的父结点去接收递归得到的结果,最后返回根节点

二、解题方法

递归三部曲

1. 确定递归函数的参数和返回值
首先确定参数为树的根结点和要删除的值,返回值为树结点

public TreeNode deleteNode(TreeNode root, int key)

2. 确定终止条件
终止条件为删除时的条件有五种情况

  1. 未找到删除结点返回null
if(root == null ) {
            return null;
        }
  1. 删除结点为叶子结点,返回null
if(root.left == null && root.right == null){
                return null;
            }
  1. 删除结点只有左孩子没有右孩子,返回删除结点的左孩子
if(root.left == null && root.right != null){
                return root.left;
            }
  1. 删除结点只有右孩子没有左孩子,返回删除结点的右孩子
if(root.left != null && root.right == null){
                return root.right;
            }
  1. 删除结点左右孩子都有,最复杂的情况,先让cur指向删除结点的右孩子,一直循环找右子树中最左的左孩子为cur,把要删除结点的左子树全部移到循环找到的cur的左孩子里,返回删除结点的右部分即可

第五种情况如果难以理解,可以结合下面的动画,将删除节点(元素7)的左孩子放到删除节点(元素7)的右子树的最左面节点(元素8)的左孩子上,就是把5为根节点的子树移到了8的左孩子的位置。要删除的节点(元素7)的右孩子(元素9)为新的根节点。.
450.删除二叉搜索树中的节点.gif
此图片取自Carl老师代码随想录

3. 确定单层递归的逻辑
只需根据二叉搜索树的顺序判断要删除的值与当前结点的大小去遍历整棵树,如果删除的值大于当前结点,说明要删除的结点在右子树里,反之,再将递归得到的子树用左右孩子接收,再返回根结点。

三、Code

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null ) {
            return null;
        }
        if(root.val == key){
            if(root.left == null){
                return root.right;
            }
            else if(root.right == null){
                return root.left;
            }else{
                TreeNode cur = root.right;
                while(cur.left != null){
                    cur = cur.left;
                }
                cur.left = root.left;
                // root = root.right;
                return root.right;
            }
        }
        if(root.val < key){
            root.right = deleteNode(root.right,key);
        }
        if(root.val > key){
            root.left = deleteNode(root.left,key);
        }
        return root;
    }
}

总结

以上就是对力扣第450题的解题思路和代码实现,需要注意的是最关键也是最复杂的第五种情况(删除一个左右孩子都不为空的节点)。删除结点操作是通过递归的返回值来实现的,在递归的上一层接住我们递归得到的子树返回值。
希望本文对你理解和解决这道题有所帮助!🌹

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值