From 2266507c27ef8514e8dcd5e03cb2120531c67f77 Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Fri, 7 Oct 2022 12:45:29 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../markilue/leecode/tree/InsertIntoBST.java | 187 ++++++++++++++++ .../leecode/tree/LowestCommonAncestor.java | 208 ++++++++++++++++++ .../leecode/tree/LowestCommonAncestorII.java | 92 ++++++++ 3 files changed, 487 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/InsertIntoBST.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestor.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestorII.java diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/InsertIntoBST.java b/Leecode/src/main/java/com/markilue/leecode/tree/InsertIntoBST.java new file mode 100644 index 0000000..25c4384 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/InsertIntoBST.java @@ -0,0 +1,187 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-10-07 12:08 + * @Description: + * TODO leecode 701题 二叉搜索树中的插入操作: + * 给定二叉搜索树(BST)的根节点root和要插入树中的值value,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。 + * 注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。 + * + * @Version: 1.0 + */ +public class InsertIntoBST { + + @Test + public void test(){ + TreeNode root = new TreeNode(4); + TreeNode TreeNode2 = new TreeNode(2); + TreeNode TreeNode3 = new TreeNode(7); + TreeNode TreeNode4 = new TreeNode(1); + TreeNode TreeNode5 = new TreeNode(3); +// TreeNode TreeNode6 = new TreeNode(0); +// TreeNode TreeNode7 = new TreeNode(8); +// TreeNode TreeNode8 = new TreeNode(7); +// TreeNode TreeNode9 = new TreeNode(4); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); +// TreeNode3.setRight(TreeNode7); +// TreeNode3.setLeft(TreeNode6); +// TreeNode5.setLeft(TreeNode8); +// TreeNode5.setRight(TreeNode9); + + System.out.println(inorderTraversal4(insertIntoBST2(root,5))); + } + + + /** + * 递归法:找到叶子结点即可将val加入 + * 速度击败100%,内存击败5.13% + * @param root + * @param val + * @return + */ + public TreeNode insertIntoBST(TreeNode root, int val) { + if(root==null) return new TreeNode(val); + if(root.val>val){ + root.left=insertIntoBST(root.left,val); + }else { + root.right=insertIntoBST(root.right,val); + } + return root; + } + + + /** + * 代码随想录无返回值递归法:仍然是找到叶子结点即可加入,利用一个parent节点记录之前的节点 + * @param root + * @param val + */ + TreeNode parent=null; + public void insert(TreeNode root, int val) { + if(root==null){ + TreeNode node = new TreeNode(val); + if(parent.val>val){ + parent.left=node; + }else { + parent.right=node; + } + return; + + } + parent=root; + if(root.val>val){ + insertIntoBST(root.left,val); + }else { + insertIntoBST(root.right,val); + } + } + + public TreeNode insertIntoBST1(TreeNode root, int val) { + if(root==null) return new TreeNode(val); + insert(root,val); + return root; + } + + + /** + * 迭代法: + * 速度击败100% 内存击败12.67% + * @param root + * @param val + * @return + */ + public TreeNode insertIntoBST2(TreeNode root, int val) { + if(root==null) return new TreeNode(val); + + TreeNode last=null; + + TreeNode temp=root; + + while (temp!=null){ + last=temp; + if(temp.val>val){ + temp=temp.left; + }else { + temp=temp.right; + } + } + + if(last.val>val){ + last.left=new TreeNode(val); + }else { + last.right=new TreeNode(val); + } + + return root; + } + + + + + + /** + * 自己尝试Morris中序遍历 + * 速度超过100%,内存超过81.82% + * @param root + */ + public static List inorderTraversal4(TreeNode root) { + + List list1 = new ArrayList(); + + TreeNode node1 = root; + TreeNode node2 = null; + + while (node1 != null) { + node2 = node1.left; + + //判断左边还有没有值 + if(node2!=null){ + //首先找到遍历node1之前的最后一个节点 + while (node2.right != null && node2.right != node1) { + node2 = node2.right; + } + + //判断是从哪个条件出来的 + if (node2.right == null) { + //如果是从第一个条件出来的,那么就是没有遍历过得节点 + + //将这个节点的右边定为node1方便以后回来,找到node1 + node2.right = node1; + //node1已经可以回溯,放心将node1左移 + node1 = node1.left; + continue; + } else { + //如果是从第二个条件出来的,那么就是遍历过的节点 + + //那么他的左边一定是遍历完了,这时将node的值加入结果 + list1.add(node1.val); + //左边遍历完了,往右移 + node1 = node1.right; + } + }else { + //左边没值了 + list1.add(node1.val); + node1=node1.right; + + } + + + } + + + return list1; + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestor.java b/Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestor.java new file mode 100644 index 0000000..8847c3c --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestor.java @@ -0,0 +1,208 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-10-07 09:30 + * @Description: TODO 力扣236题 二叉树的最近公共祖先: + * 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + * 百度百科中最近公共祖先的定义为: + * “对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + * @Version: 1.0 + */ +public class LowestCommonAncestor { + + @Test + public void test() { + TreeNode root = new TreeNode(3); + TreeNode TreeNode2 = new TreeNode(5); + TreeNode TreeNode3 = new TreeNode(1); + TreeNode TreeNode4 = new TreeNode(6); + TreeNode TreeNode5 = new TreeNode(2); + TreeNode TreeNode6 = new TreeNode(0); + TreeNode TreeNode7 = new TreeNode(8); + TreeNode TreeNode8 = new TreeNode(7); + TreeNode TreeNode9 = new TreeNode(4); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + TreeNode3.setRight(TreeNode7); + TreeNode3.setLeft(TreeNode6); + TreeNode5.setLeft(TreeNode8); + TreeNode5.setRight(TreeNode9); + + System.out.println(lowestCommonAncestor1(root, TreeNode2, TreeNode3).val); + + } + + @Test + public void test1() { + TreeNode root = new TreeNode(3); + TreeNode TreeNode2 = new TreeNode(5); + TreeNode TreeNode3 = new TreeNode(1); + TreeNode TreeNode4 = new TreeNode(6); + TreeNode TreeNode5 = new TreeNode(2); + TreeNode TreeNode6 = new TreeNode(0); + TreeNode TreeNode7 = new TreeNode(8); + TreeNode TreeNode8 = new TreeNode(7); + TreeNode TreeNode9 = new TreeNode(4); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + TreeNode3.setRight(TreeNode7); + TreeNode3.setLeft(TreeNode6); + TreeNode5.setLeft(TreeNode8); + TreeNode5.setRight(TreeNode9); + + System.out.println(lowestCommonAncestor1(root, TreeNode2, TreeNode9).val); + + } + + + /** + * 本人思路:递归法:用一个boolean值记录是否找到了p,q如果找到了就可以返回当前叶子节点 + * 速度超过5.09%,内存超过38.12% + * 慢之处在于即使找到了还需要继续遍历,因为本人这种方式是自顶向下遍历的方式,找到的不一定是最近公共祖先,只能通过不断遍历的方式确定 + * + * @param root + * @param p + * @param q + * @return + */ + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + find(root, p, q); + return result; + } + + + TreeNode result = null; + boolean flag = false; + + public boolean find(TreeNode root, TreeNode p, TreeNode q) { + if (root == null) { + return false; + } + + //记录两个树值是否找到 + boolean flagp = false; + boolean flagq = false; + + + if (p != null) { + if (root.val == p.val) { + flagp = true; + } else { + //这里只找p + flagp = find(root.left, p, null) || find(root.right, p, null); + } + } + + if (q != null) { + if (root.val == q.val) { + flagq = true; + } else { + flagq = find(root.left, null, q) || find(root.right, null, q); + } + } + + + if (flagp && flagq) { + result = root; + find(root.left, p, q); + find(root.right, p, q); +// flag = true; + } + + + return flagp || flagq; + + + } + + + /** + * 代码随想录思路:递归法:最近节点->如果能自底向上查找就好了,因此可以使用回溯,回溯的过程就是自底向上,二叉树的后续遍历就符合回溯过程,先处理的一定是叶子结点 + * 速度超过100%,内存超过28.79% + *

+ * 递归三部曲: + * 1)确定递归函数返回值和参数:TreeNode 找到了就返回这个节点 + * 2)确定终止条件:找到p或q或者空节点就返回 + * 3)确定单层递归逻辑:通过递归函数确定该节点是不是公共节点 + * + * + * @param root + * @param p + * @param q + * @return + */ + public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) { + //这里之所以可以直接等于就返回,是因为后边还有判断right边有没有,如果right边没有,那么一定是在left边的子树上,这里默认两个子树一定存在。 + if (root == p || root == q || root == null) return root; + + TreeNode left = lowestCommonAncestor1(root.left, p, q); + TreeNode right = lowestCommonAncestor1(root.right, p, q); + + //如果left和right都有值,那么证明当前节点就是最近 + //反之如果任意没有值就返回另一边 + if(left!=null&&right!=null){ + return root; + }else if(left==null){ + return right; + }else { + return left; + } + } + + + + Map parent = new HashMap(); + Set visited = new HashSet(); + + public void dfs(TreeNode root) { + if (root.left != null) { + parent.put(root.left.val, root); + dfs(root.left); + } + if (root.right != null) { + parent.put(root.right.val, root); + dfs(root.right); + } + } + + /** + * 官方另一种解法:利用map记录父节点,q不断回溯来寻找最近节点 + * @param root + * @param p + * @param q + * @return + */ + public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) { + dfs(root); + while (p != null) { + visited.add(p.val); + p = parent.get(p.val); + } + while (q != null) { + if (visited.contains(q.val)) { + return q; + } + q = parent.get(q.val); + } + return null; + } + + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestorII.java b/Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestorII.java new file mode 100644 index 0000000..b6be9fb --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/LowestCommonAncestorII.java @@ -0,0 +1,92 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-10-07 11:29 + * @Description: TODO leecode 235题 二叉搜索树的最近公共祖先: + * 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 + * 百度百科中最近公共祖先的定义为: + * “对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + * @Version: 1.0 + */ +public class LowestCommonAncestorII { + + @Test + public void test(){ + TreeNode root = new TreeNode(6); + TreeNode TreeNode2 = new TreeNode(2); + TreeNode TreeNode3 = new TreeNode(8); + TreeNode TreeNode4 = new TreeNode(0); + TreeNode TreeNode5 = new TreeNode(4); + TreeNode TreeNode6 = new TreeNode(7); + TreeNode TreeNode7 = new TreeNode(9); + TreeNode TreeNode8 = new TreeNode(3); + TreeNode TreeNode9 = new TreeNode(5); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + TreeNode3.setRight(TreeNode7); + TreeNode3.setLeft(TreeNode6); + TreeNode5.setLeft(TreeNode8); + TreeNode5.setRight(TreeNode9); + + System.out.println(lowestCommonAncestor(root, TreeNode2, TreeNode3).val); + System.out.println(lowestCommonAncestor(root, TreeNode2, TreeNode5).val); + } + + + /** + * 二叉搜索树的特性:root节点比左边大,右边小 + * 速度击败36.19%,内存击败82.68% + * @param root + * @param p + * @param q + * @return + */ + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + + + if (root == null ) return root; + if(root.val>p.val&&root.val>q.val){ + return lowestCommonAncestor(root.left, p, q); + } + + if(root.val ancestor.val && q.val > ancestor.val) { + ancestor = ancestor.right; + } else { + break; + } + } + return ancestor; + } + + + + +}