给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
示例:
root = [5,3,6,2,4,null,7]
key = 3
5
/
3 6
/
2 4 7
给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
5
/
4 6
/
2 7
另一个正确答案是 [5,2,6,null,4,null,7]。
5
/
2 6
4 7
思路:
• 二叉搜索树,因为其是有序的,左边的节点都比根节点小,右边的节点都比根节点大;
• 要删除某一个节点,分情况讨论:1. 如果是叶节点,则直接删除;
• 2.如果是非叶子节点,且有左儿子节点,则找到其前驱节点(因为前驱节点是:在比当前节点小的节点中,最大的一个,将他放到当前节点位置,其左边的儿子节点还是都比他小,满足二叉搜索树定义);
• 3.如果是非叶子节点,且没有左儿子,只有右儿子节点,则找到其后继节点(因为后继节点是:比当前节点大的节点中,最小的那一个,将他替换后,满足右边都比他大);
• 将当前节点的值,换为前驱/后继节点的值,再递归删除这个前驱/后继节点即可。
class Solution { public TreeNode deleteNode(TreeNode root, int key) { if(root == null) return null; if(key < root.val) root.left = deleteNode(root.left, key); //key在左边子节点 else if(key > root.val) root.right = deleteNode(root.right, key); //key在右边子节点 else{ // 当前节点就是需要删除的节点 if(root.left == null && root.right == null) return null;//如果是叶子节点,直接删除即可 else if(root.left != null){ //非叶子节点,如果有左儿子节点,则寻找其前驱节点 int num = preNode(root); root.val = num; //将前驱节点的值,赋给当前节点,删掉这个前驱节点 root.left = deleteNode(root.left, num); }else{ // 非叶子节点,如果没有左儿子,但有右儿子节点,寻找其后继节点 int num = afterNode(root); root.val = num; //同理,删掉后继节点 root.right = deleteNode(root.right, num); } } return root; //返回根节点 } private int preNode(TreeNode root){ //寻找根节点的前驱节点 root = root.left; while(root.right != null) root = root.right; return root.val; } private int afterNode(TreeNode root){ //寻找根节点的后继节点 root = root.right; while(root.left != null) root = root.left; return root.val; } }