LeetCode 删除二叉搜索树中的节点
@author:Jingdai
@date:2020.12.06
题目描述(450题)
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
题目分析
对于一个二叉搜索树,要删除树中一个节点,分3种情况。
情况1:如果要删除的节点是叶子节点,比如图中节点12,可以直接删除。
情况2:如果要删除的节点只有一个子树,比如图中节点5,可以将该节点的子树提上去就行。
情况3:如果要删除的节点有两个孩子,比如图中节点15。对于这种情况,为了使搜索二叉树的结构不被破坏,需要从删除节点的右子树中找到最小的节点,将这个节点的值赋给删除节点,然后再删除右子树中最小的节点。(当然从删除节点的左子树中找最大的节点也行)
思路及代码
有两种方式实现,一种是递归,一种是非递归。
递归方法
我们先看函数的定义,删除一个节点,并返回二叉搜索树(有可能被更新)的根节点的引用。
若 key 的值比当前节点的值大,说明应该去右子树中删除,将右子树删除 key 后的根节点返回,并将结果赋值给当前节点的右子树。即: root.right = deleteNode(root.right, key);
若 key 的值比当前节点的值小,说明应该去左子树中删除,将左子树删除 key 后的根节点返回,并将结果赋值给当前节点的左子树。即: root.left = deleteNode(root.left, key);
若 key 的值和当前节点的值相等,说明应该删除了,就按照题目分析中的三种情况进行删除。
完整的代码如下。
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null)
return null;
if (key == root.val) {
// 1.leaf
if (root.left == null && root.right == null) {
return null;
} else if (root.left == null) { // 2.left is null
return root.right;
} else if (root.right == null) { // 3.right is null
return root.left;
} else { // 4.left/right r not null
TreeNode rightTreeMin = root.right;
while (rightTreeMin.left != null) {
rightTreeMin = rightTreeMin.left;
}
root.val = rightTreeMin.val;
root.right = deleteNode(root.right, rightTreeMin.val);
}
} else if (key > root.val) {
root.right = deleteNode(root.right, key);
} else {
root.left = deleteNode(root.left, key);
}
return root;
}
非递归方法
思路还是类似,当 key 的值比当前节点的值大时,就去右子树中查找;当 key 的值比当前节点值小时,就去左子树中查找,直到找到要删除的节点。但是注意非递归方法中还需要记录当前节点的父节点,用于最后的删除,因为递归方法是在递归函数的上一层进行处理的,所以不用记录。当删除节点的左右子树都不为空时,父节点有两种情况。如图左,当要删除节点20时,右子树最小的节点就是 cur
的右孩子,pre
等于 cur
,此时删除右子树最小节点应该让 pre
的右孩子等于右子树最小节点的右孩子,即:pre.right = rightTreeMin.right
;如图右,当右子树最小节点不是 cur
的右孩子时,如删除节点15时,删除右子树最小节点应该让 pre
的左孩子等于右子树最小节点的右孩子,即:pre.left = rightTreeMin.right
。
同时,非递归方法还要对删除节点是根节点进行特殊判断,否则 pre
会出现空指针异常。对根节点进行判断时,不用考虑左右子树都不为空的情况,因为这种情况会对 pre
重新赋值,不会出现空指针异常。
最后的代码如下。
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null)
return null;
if (root.val == key) {
if (root.left == null && root.right == null) {
return null;
} else if (root.left == null) {
return root.right;
} else if (root.right == null) {
return root.left;
}
}
TreeNode cur = root;
TreeNode pre = null;
while (cur != null) {
if (key == cur.val) {
// leaf
if (cur.left == null && cur.right == null) {
if (pre.left == cur)
pre.left = null;
if (pre.right == cur)
pre.right = null;
} else if (cur.left == null) {
if (pre.left == cur)
pre.left = cur.right;
if (pre.right == cur)
pre.right = cur.right;
} else if (cur.right == null) {
if (pre.left == cur)
pre.left = cur.left;
if (pre.right == cur)
pre.right = cur.left;
} else {
TreeNode rightTreeMin = cur.right;
pre = cur;
while (rightTreeMin.left != null) {
pre = rightTreeMin;
rightTreeMin = rightTreeMin.left;
}
cur.val = rightTreeMin.val;
// delete rightTreeMin
if (pre == cur) {
pre.right = rightTreeMin.right;
} else {
pre.left = rightTreeMin.right;
}
}
break;
} else if (key > cur.val) {
pre = cur;
cur = cur.right;
} else {
pre = cur;
cur = cur.left;
}
}
return root;
}