给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
中等难度。有两种方式一种是递归,另一种是非递归。
我们首先要找到被值相同的节点A:
-
如果该节点的一个子树为空,那么用另一个子树的根节点代替当前节点的位置即可。
-
如果该节点的两个子树都为空,那么删除该节点即可,这就是第一种情况的特例。
-
如果该节点的左右子树都不为null,那么找到左子树的最大节点或者右子树的最小节点B,此时剩下的步骤有两个思路
- 第一种是方法是将A的左子树赋值给B的左子树(如果B是右子树的最小节点),或者将A的右子树赋值给B的右子树(如果B是左子树的最大节点)。
- 另一种方法是将找到的节点B的值赋给节点A,然后删除找到的节点B,这样的好处是不会增加树的层级。
迭代算法:
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) {
return null;
}
//node 值相等的节点 pre 被删除节点的前驱节点 removeNode 真正被删除的节点
TreeNode node = root, pre = null, removeNode = root;
while (node != null) {
//找到了key相等的节点
if (node.val == key) {
//开始查找真正需要删除的节点
removeNode = node;
//查找左子树的最大节点
if (removeNode.left != null && removeNode.right != null) {
pre = removeNode;
removeNode = removeNode.left;
while (removeNode.right != null) {
pre = removeNode;
removeNode = removeNode.right;
}
}
break;
}
pre = node;
node = node.val > key ? node.left : node.right;
}
//如果没找到,那么返回root
if (node == null) {
return root;
}
//交换值,然后删除真正需要被删除的节点
node.val = removeNode.val;
if (pre != null) {
if (removeNode == pre.left) {
pre.left = removeNode.left != null ? removeNode.left : removeNode.right;
}
if (removeNode == pre.right) {
pre.right = removeNode.left != null ? removeNode.left : removeNode.right;
}
return root;
}
root = removeNode.left != null ? removeNode.left : removeNode.right != null ? removeNode.right : null;
return root;
}
递归算法,更好理解,就是一个dfs递归遍历:
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) {
return null;
}
if (root.val > key) {
root.left = deleteNode(root.left, key);
} else if (root.val < key) {
root.right = deleteNode(root.right, key);
} else {
//1 2 该节点的至少一个子树为空,返回另一个子树
if (root.left == null || root.right == null)
return root.left != null ? root.left : root.right != null ? root.right : null;
else {
//3 该节点的左右子树都不为null
//查找右子树的最小节点p
TreeNode p = root.right;
while (p.left != null) {
p = p.left;
}
//root的左子树赋值给p的左子树
p.left = root.left;
root = root.right;
return root;
}
}
return root;
}