如何删除二叉搜索树中的节点

题目再现

力扣:450
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
1.首先找到需要删除的节点;
2.如果找到了,删除它。

思路:

我们可以先观察一下二叉树删除节点后有几种情况
在这里插入图片描述

情况1:左右子树都存在

例如删除图中的 7 节点
在这里插入图片描述
这是最复杂的一种,左右节点都有,将右子树置于原节点位置,原左节点置为原右子树最左节点的左子树

情况2: 只有左子树或右子树

只要补位即可 例如删除图中的 13 节点
在这里插入图片描述

情况3: 左右子树都没有

直接删除即可 例如图中的 2 节点
在这里插入图片描述

情况4: 图中没有这个节点

这个就不多说了,直接返回即可
如删除图中的 20 节点

试错

好了,找到规律了我们不难写出如下代码

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func deleteNode(root *TreeNode, key int) *TreeNode {
    if root == nil{
        return root
    } 
    //寻找对应值
    if root.Val > key{
        deleteNode(root.Left,key)
    }else if root.Val < key{
        deleteNode(root.Right,key)
    }else{
        if root.Left == nil && root.Right == nil{
            root = nil
        }else if root.Left != nil && root.Right == nil{
            *root = *root.Left // 使用*root 解引用出源节点
        }else{ // 左右子树都不为空的情况包含了仅有左子树为空的情况
            left := FindLeft(root.Right) // 找到右子树的最左节点
            left.Left = root.Left
            *root = *root.Right
        }
    }
    return root
}
func FindLeft(root *TreeNode) *TreeNode{
    if root.Left == nil{
        return root
    }else{
        return FindLeft(root.Left)
    }
}

诶,此时出现一个问题

if root.Left == nil && root.Right == nil{
            root = nil
        }

这段代码不起作用,原因很简单,此处root是传值,没有解引用不会影响到原二叉树上的值,但是如果使用下述代码

if root.Left == nil && root.Right == nil{
            *root = nil
        }

会报错,在Go中不允许这样操作,因为因为 *root 是一个 TreeNode 类型的值,而不是一个指针
如果想要这样操作的话,就要修改函数声明了,将函数声明改为

func deleteNode(root **TreeNode, key int) *TreeNode

但是不符合题意了,故放弃这种想法

那有没有别的方法呢,有!

我们换个角度想,将本节点置为空,将等于是将父节点的左/右节点置为空,这样就能在递归的时候将函数返回值用上了

代码实现(Go)

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func deleteNode(root *TreeNode, key int) *TreeNode {
    if root == nil{
        return root
    }
    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 == nil && root.Right == nil{
            root = nil
        }else if root.Left != nil && root.Right == nil{
            root = root.Left
        }else{
            left := FindLeft(root.Right)
            left.Left = root.Left
            root = root.Right
        }
    }
    return root
}
func FindLeft(root *TreeNode) *TreeNode{
    if root.Left == nil{
        return root
    }else{
        return FindLeft(root.Left)
    }
}

这样也不需要解引用操作了,搞定!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值