diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/DeleteNodeII.java b/Leecode/src/main/java/com/markilue/leecode/tree/DeleteNodeII.java new file mode 100644 index 0000000..52fe779 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/DeleteNodeII.java @@ -0,0 +1,254 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.List; + +import static com.markilue.leecode.tree.InorderTraversal.inorderTraversal1; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-10-08 09:22 + * @Description: TODO leecode 450题 删除二叉搜索树中的节点: + * 给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的key对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。 + * 一般来说,删除节点可分为两个步骤: + * 1)首先找到需要删除的节点; + * 2)如果找到了,删除它。 + * @Version: 1.0 + */ +public class DeleteNodeII { + + @Test + public void test(){ + TreeNode root = new TreeNode(4); + TreeNode TreeNode2 = new TreeNode(2); + TreeNode TreeNode3 = new TreeNode(6); + TreeNode TreeNode4 = new TreeNode(1); + TreeNode TreeNode5 = new TreeNode(3); + TreeNode TreeNode6 = new TreeNode(5); + + root.setLeft(TreeNode2); + root.setRight(TreeNode3); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + TreeNode3.setLeft(TreeNode6); + + deleteNode1(root, 5); + + List integers = inorderTraversal1(root); + + System.out.println(integers); + } + + + @Test + public void test1(){ + TreeNode root = new TreeNode(5); + TreeNode TreeNode2 = new TreeNode(3); + TreeNode TreeNode3 = new TreeNode(6); + TreeNode TreeNode4 = new TreeNode(2); + TreeNode TreeNode5 = new TreeNode(4); + TreeNode TreeNode6 = new TreeNode(7); + + root.setLeft(TreeNode2); + root.setRight(TreeNode3); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + TreeNode3.setRight(TreeNode6); + + deleteNode1(root, 3); + + List integers = inorderTraversal1(root); + + System.out.println(integers); + } + + + /** + * 迭代法:找到该节点,然后找到该节点的右子节点的最左节点将其替换,在删除其最左节点 + * 参考官方迭代法的简洁写法,可以发现官方是使用了child节点记录需要删除的子节点,使得代码统一,同时也避免了两重if + * 速度击败100%,内存击败78.04% + * @param root + * @param key + * @return + */ + public TreeNode deleteNode(TreeNode root, int key) { + + //后面的逻辑中包含了这个 +// if (root == null) { +// return null; +// } + + //TODO 寻找该节点 + TreeNode temp = root; + TreeNode father=null; + while (temp != null && temp.val != key) { + father=temp; + if (temp.val > key) { + temp = temp.left; + } else { + temp = temp.right; + } + } + + //TODO 判断是什么条件出来的 + //这里是没找到 + if(temp==null){ + return root; + } + + TreeNode change=temp; //记录需要改变的这个节点 + + //有两个子节点的情况 + if (temp.right!=null){ + father=temp; + temp=temp.right; + + //寻找该右子节点的最左节点 + while (temp.left!=null){ + father=temp; + temp=temp.left; + } + change.val=temp.val; //将最左节点的值赋值给需要改变的节点,接下来删除最左节点 + change=temp; + } + + //删除的是根节点 + if(father==null){ + if(temp.left!=null){ + return temp.left; + } + return null; + } + + + //TODO 处理单节点的情况 + + //继续删除temp节点,这里要判断他有没有左节点,如果有则是从temp.right!=null出来的 + if(temp.left!=null){ + if(father.left==temp){ + father.left=temp.left; + }else { + father.right=temp.left; + } + } + + + + //继续删除temp节点,这里要判断他有没有有节点,如果有则是进入过temp.right!=null出来的 + if (temp.right!=null){ + if(father.left==temp){ + father.left=temp.right; + }else { + father.right=temp.right; + } + } + + //处理叶子节点的情况 + if(temp.left==null&&temp.right==null){ + if(father.left==temp){ + father.left=null; + }else { + father.right=null; + } + } + + return root; + + + } + + + /** + * 递归法:找到该节点,然后找到该节点的右子节点的最左节点将其替换,在删除其最左节点 + * + * 速度击败100%,内存击败19.68% + * @param root + * @param key + * @return + */ + public TreeNode deleteNode1(TreeNode root, int key) { + + if(root==null)return null; + + if(root.valkey){ + root.left=deleteNode1(root.left,key); + return root; + }else { + TreeNode result=root; + TreeNode father=null; + //需要删除的节点 + if(root.right!=null){ + father=root; + TreeNode temp=root.right; + while (temp.left!=null){ + father=temp; + temp=temp.left; + } + root.val=temp.val; + root=temp; + } + TreeNode child=null; + if(root.left!=null)child= root.left; + if(root.right!=null)child= root.right; + if(father==null) return child; + else if(father.left==root)father.left=child; + else father.right=child; + + return result; + } + + } + + + /** + * 官方递归法: + * @param root + * @param key + * @return + */ + public TreeNode deleteNode2(TreeNode root, int key) { + if (root == null) { + return null; + } + if (root.val > key) { + root.left = deleteNode(root.left, key); + return root; + } + if (root.val < key) { + root.right = deleteNode(root.right, key); + return root; + } + if (root.val == key) { + if (root.left == null && root.right == null) { + return null; + } + if (root.right == null) { + return root.left; + } + if (root.left == null) { + return root.right; + } + TreeNode successor = root.right; + while (successor.left != null) { + successor = successor.left; + } + + //这里删除节点,然后将删完的好节点付给successor并返回(是否可以直接将root的值变为successor的值?) + root.right = deleteNode(root.right, successor.val); + successor.right = root.right; + successor.left = root.left; + return successor; + } + return root; + } + + + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/TrimBST.java b/Leecode/src/main/java/com/markilue/leecode/tree/TrimBST.java new file mode 100644 index 0000000..b9c24e1 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/TrimBST.java @@ -0,0 +1,130 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +import static com.markilue.leecode.tree.InorderTraversal.inorderTraversal1; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-10-08 11:45 + * @Description: TODO 力扣669题 修剪二叉搜索树: + * 给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。 + * 修剪树 不应该改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。 + * 所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。 + * @Version: 1.0 + */ +public class TrimBST { + + @Test + public void test() { + TreeNode root = new TreeNode(3); + TreeNode TreeNode2 = new TreeNode(0); + TreeNode TreeNode3 = new TreeNode(4); + TreeNode TreeNode4 = new TreeNode(2); + TreeNode TreeNode5 = new TreeNode(1); +// TreeNode TreeNode6 = new TreeNode(7); + + root.setLeft(TreeNode2); + root.setRight(TreeNode3); + TreeNode2.setRight(TreeNode4); + TreeNode4.setLeft(TreeNode5); +// TreeNode3.setRight(TreeNode6); + + TreeNode node = trimBST2(root, 1, 3); + + List integers = inorderTraversal1(node); + + System.out.println(integers); + } + + /** + * 初步思路:使用deleteNode一个一个删除 + * + * @param root + * @param low + * @param high + * @return + */ + public TreeNode trimBST(TreeNode root, int low, int high) { + for (int i = low; i < high; i++) { + DeleteNodeII deleteNodeII = new DeleteNodeII(); + root = deleteNodeII.deleteNode(root, i); + } + return root; + } + + + + /** + * 进阶思路:代码随想录递归法 + * 利用二叉搜索树的特性实现快速删除:找到low,将low.right直接代替low;找到high,将high.left直接代替high + * 其他时候比他小就返回修完右边的值,比他大就返回修完左边的值 + * 速度击败100% 内存击败18.83% + * @param root + * @param low + * @param high + * @return + */ + public TreeNode trimBST2(TreeNode root, int low, int high) { + if (root == null) return null; + if (root.val < low) return trimBST2(root.right, low, high); + if (root.val > high) return trimBST2(root.left, low, high); + root.left = trimBST2(root.left, low, high); + root.right = trimBST2(root.right, low, high); + return root; + } + + + /** + * 进阶思路:代码随想录迭代法 + * 利用二叉搜索树的特性实现快速删除:先找到第一个在区间内的数,以此为根基,分别处理其左节点和右节点,左节点往右移;右节点往左移 + * 其他时候比他小就返回修完右边的值,比他大就返回修完左边的值 + * 速度击败100% 内存击败38.55% + * @param root + * @param low + * @param high + * @return + */ + public TreeNode trimBST3(TreeNode root, int low, int high) { + if(root==null)return null; + + //处理头结点,让root移动到一个在区间内的数 + while (root!=null&&(root.valhigh)){ + if(root.valhigh){ + cur.right=cur.right.left; + } + //处理完毕,安心将cur右移,继续判断其左节点会不会超过 + cur=cur.right; + //这里不需要判断左节点,因为root在区间内,那么右子树最小也就是root,所以不会超过其左界 + } + + //全部处理完毕,安心返回root + return root; + } +}