题目再现
力扣: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)
}
}
这样也不需要解引用操作了,搞定!