代码随想录_删除二叉树搜索树中的节点

代码随想录Day20_删除二叉树搜索树中的节点

BG:转眼间已经Day21了,刷代码随想录ing。本人是25届秋招选手,目标后端开发,个人公众号为TccdoeT。关注我,一起交流经验吧!

在这里插入图片描述

1.题目描述

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

在这里插入图片描述
在这里插入图片描述

2.题目分析

我认为该题算是二叉搜索树中比较经典的题目了,其实思路很简单,找到并删除,难点在于如何**正确的分类讨论来使得删除完后的树依然组织为一个合法的二叉搜索树**(注意这个地方,待会会考)。递归法会相对容易,迭代法更考验书写代码的能力,这次也会讲解下迭代法的部分。

首先我们根据题目示例来看大概会存在以下几种情况:

  • 删除节点为根节点
    • 有左树,无右树
    • 有右树,无左树
    • 左右子树都无
    • 左右子树都有
  • 删除节点为非根节点
    • 有左树,无右树
    • 有右树,无左树
    • 左右子树都无
    • 左右子树都有

PS:为什么会得到这些分类的,对于我来说,根和非根的分类是比较自然的,至于接下去的小分类想一想也大概想得到.

继续分析下:对于根和非根分类的原因是需要考虑其前驱节点的关联。以下代码是对小类的分类

//对于每个小类而言,假设curr为当前需要删除的节点,这种判断的方式真的优雅,可以积累下
//1.
if(curr.left==null){
    return curr.right;
}
//2.
if(curr.left==null){
    return curr.right;
}
//3.左右子树均不为空
TODO

综上:对于根节点我们直接进行操作就好,分根节点需要前驱节点进行指向,待删除节点的左右子树都存在先放着。我们可以得到如下代码

2.1递归法
class Solution {
  public TreeNode deleteNode(TreeNode root, int key) {
    if (root == null) return root;
    //1.第一次遍历到此部分就是对根节点处理
    if (root.val == key) {
      	if (root.left == null) {
        	return root.right;
      	if (root.right == null) {
        	return root.left;
         } 
         //2.TODO:左右子树均存在的情况
    }
    //后续这里需要明确指向关系,想一想是如何操作的
    if (root.val > key) root.left = deleteNode(root.left, key);
    if (root.val < key) root.right = deleteNode(root.right, key);
    return root;
  }
}

重点:考虑对待删除节点的左右子树都存在的情况。注意题目只要求变化后的二叉搜索树合法即可,不需要考虑深度或者平衡等等特性,这给我们的操作带来了极大的便利

假设我们需要删除 10 这个节点

在这里插入图片描述 在这里插入图片描述

可以有以下两种调整的做法,思考一下,你悟了吗

在这里插入图片描述
在这里插入图片描述

> //假设我们选择左边这种方式,将待删除节点的左孩子接到待删除节点的右孩子的最左孩子。观察以下代码
> TreeNode cur = root.right;
> //找到右子树的最左孩子
> while (cur.left != null) {
>      cur = cur.left;
> }
> //连接
> cur.left = root.left;
> //返回待删除节点的右子树
> return root.right;
>         
2.2迭代法[冗长,但是好理解]

迭代法是在很难写,难点在哪里,难在我们在删除节点后,需要保留它的前驱关系。观看下面这张图,假设需要删除node.val = 5,删除节点的方法和上面相同,但我们还需要让其前驱节点指向他,并且左右子树指向的操作不一样!使用递归法只需要两个指向即可,迭代法需要自己来维护前一个节点

在这里插入图片描述

解决方案:既然我们需要记录当前节点的前一个,那使用栈这种结构不是刚刚好!当遍历到5需要删除时,pop()出10来保证指向关系

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root==null){
            return null;
        }
        //1.对根节点进行特判
        if(root.val==key){
            if(root.left==null){
                return root.right;
            }
            if(root.right==null){
                return root.left;
            }
            //如果都不为空的话:尽量找到叶子节点然后再进行操作
            TreeNode node=root.right;
            while(node.left!=null){
                node=node.left;
            }
            node.left=root.left;
            return root.right;
            
        }
        //2.对非根节点进行处理
        TreeNode tmp=root;
        Deque<TreeNode> stack=new LinkedList<>();//利用栈来存储上一个节点
        while(root!=null){
            //2.1如果找到了需要删除的节点
            if(root.val == key){
                //2.1.1如果左子树为空
                if(root.left==null){
                    TreeNode node=stack.pop();
                    //2.1.1.1这里要判断的是我们要删除的节点是前驱节点的左孩子还是右孩子,因为操作是不一致的
                    if(node.left==root){
                        node.left=root.right;
                    }else if(node.right==root){
                        node.right=root.right;
                    }
                    return tmp;
                }
                //2.1.2如果右子树为空
                if(root.right==null){
                    TreeNode node=stack.pop();
                    //2.1.2.1同上
                    if(node.left==root){
                        node.left=root.left;
                    }else if(node.right==root){
                        node.right=root.left;
                    }
                    return tmp;
                }
                //2.1.3如果左右子树都不会空
                TreeNode node=stack.pop();
                //2.1.3.1同上
                if(node.left==root){
                    TreeNode x=root.right;
                    while(x.left!=null){
                        x=x.left;
                    }
                    x.left=root.left;
                    node.left=root.right;
                }else if(node.right==root){
                    TreeNode x=root.right;
                    while(x.left!=null){
                        x=x.left;
                    }
                    x.left=root.left;
                    node.right=root.right;
                }
                return tmp;
            }else if(root.val > key){
                //2.2没找到就push当前节点,并继续
                stack.push(root);
                root=root.left;
            }else if(root.val < key){
 			   //2.2没找到就push当前节点,并继续
                stack.push(root);
                root=root.right;
            }
        }
        return tmp;
    }
}

在这里插入图片描述

以上就是本题的全部内容了,如果对你有帮助可以留言鼓励下哦## 删除二叉树搜索树中的节点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值