diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/IsValidBST.java b/Leecode/src/main/java/com/markilue/leecode/tree/IsValidBST.java new file mode 100644 index 0000000..352330b --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/IsValidBST.java @@ -0,0 +1,221 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.Stack; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-26 11:06 + * @Description: TODO 力扣98题 验证二叉搜索树: + * 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 + * 有效 二叉搜索树定义如下: + * 1.节点的左子树只包含 小于 当前节点的数。 + * 2.节点的右子树只包含 大于 当前节点的数。 + * 3.所有左子树和右子树自身必须也是二叉搜索树。 + * @Version: 1.0 + */ +public class IsValidBST { + + @Test + public void test() { + TreeNode root = new TreeNode(2); + TreeNode TreeNode2 = new TreeNode(1); + TreeNode TreeNode3 = new TreeNode(3); +// TreeNode TreeNode4 = new TreeNode(5); + + + TreeNode TreeNode5 = new TreeNode(5); + TreeNode TreeNode6 = new TreeNode(1); + TreeNode TreeNode7 = new TreeNode(4); + TreeNode TreeNode8 = new TreeNode(3); + TreeNode TreeNode9 = new TreeNode(6); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); +// TreeNode2.setLeft(TreeNode4); + + + TreeNode5.setLeft(TreeNode6); + TreeNode5.setRight(TreeNode7); + TreeNode7.setLeft(TreeNode8); + TreeNode7.setRight(TreeNode9); + + System.out.println(isValidBST1(root)); + System.out.println(isValidBST1(TreeNode5)); + } + + @Test + public void test1() { + TreeNode root = new TreeNode(0); +// TreeNode TreeNode2 = new TreeNode(-1); + TreeNode TreeNode3 = new TreeNode(1); +// TreeNode TreeNode4 = new TreeNode(5); + + + root.setRight(TreeNode3); +// root.setLeft(TreeNode3); +// TreeNode2.setLeft(TreeNode4); + + + System.out.println(isValidBST1(root)); + + } + + @Test + public void test2() { + TreeNode root = new TreeNode(5); + TreeNode TreeNode2 = new TreeNode(4); + TreeNode TreeNode3 = new TreeNode(6); + TreeNode TreeNode4 = new TreeNode(3); + TreeNode TreeNode5 = new TreeNode(7); + + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode3.setLeft(TreeNode4); + TreeNode3.setRight(TreeNode5); + + + System.out.println(isValidBST1(root)); + + } + + @Test + public void test3() { + TreeNode root = new TreeNode(-2147483648); + TreeNode TreeNode2 = new TreeNode(4); + TreeNode TreeNode3 = new TreeNode(2147483647); + TreeNode TreeNode4 = new TreeNode(3); + TreeNode TreeNode5 = new TreeNode(7); + + + root.setRight(TreeNode3); +// root.setLeft(TreeNode2); +// TreeNode3.setLeft(TreeNode4); +// TreeNode3.setRight(TreeNode5); + + + System.out.println(isValidBST(root)); + + } + + @Test + public void test4() { + TreeNode root = new TreeNode(3); + TreeNode TreeNode2 = new TreeNode(1); + TreeNode TreeNode3 = new TreeNode(5); + TreeNode TreeNode4 = new TreeNode(0); + TreeNode TreeNode5 = new TreeNode(2); + TreeNode TreeNode6 = new TreeNode(4); + TreeNode TreeNode7 = new TreeNode(6); + + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + TreeNode3.setLeft(TreeNode6); + TreeNode3.setRight(TreeNode7); + + + System.out.println(isValidBST1(root)); + + } + + /** + * 递归法 + * 速度超过100%,内存超过67.95% + * + * @param root + * @return + */ + public boolean isValidBST(TreeNode root) { + return isValid(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + + + //limit即一个数的节点一定大于他左节点的右节点,小于他右节点的左节点 + public boolean isValid(TreeNode root, Long leftLimit, Long rightLimit) { + + //第一个条件是为了第一次root等于null的情况,理论上不会 + if (root.left == null && root.right == null) return true; + + boolean flag1 = false; + if (root.left == null) { + flag1 = true; + } else { + if (root.val > root.left.val && root.left.val > leftLimit) { + flag1 = isValid(root.left, leftLimit, Long.valueOf(root.val)); + } + } + //快速返回 + if (!flag1) { + return false; + } + boolean flag2 = false; + if (root.right == null) { + flag2 = true; + } else { + if (root.val < root.right.val && root.right.val < rightLimit) { + flag2 = isValid(root.right, Long.valueOf(root.val), rightLimit); + } + } + return flag2; + } + + + /** + * 代码随想录递归法 + * 速度超过100%,内存超过67.95% + * + * @param root + * @return + */ + TreeNode pre = null;//记录前一个节点 + public boolean isValidBST2(TreeNode root) { + if (root == null) return true; + boolean left = isValidBST2(root.left); + + if (pre != null && pre.val >= root.val) { + return false; + } + pre = root; //记录前一个节点 + boolean right = isValidBST2(root.right); + return left && right; + } + + + /** + * 迭代法:由于左边节点和右边节点的界限不相同,因此不能使用层序遍历,这里使用中序遍历 + * 速度超过19.02%,内存超过11.02% + * + * @param root + * @return + */ + public boolean isValidBST1(TreeNode root) { + if (root == null) { + return false; + } + + Stack stack = new Stack<>(); + + TreeNode pre = null;//记录前一个节点 + TreeNode cur = root; +// + while (!stack.isEmpty() || cur != null) { + if (cur != null) { + stack.push(cur); + cur=cur.left; + } else { + cur = stack.pop(); + if (pre != null && cur.val <= pre.val) return false; //这里是1)将左子节点小于本节点 2)本节点的右子节点的所有节点都大于本节点 这两个逻辑统一:通过记录前一个指针来完成,这个操作似乎只能通过中序遍历来完成 + pre = cur;//保存访问的前一个节点 + cur = cur.right; + } + } + return true; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/MergeTrees.java b/Leecode/src/main/java/com/markilue/leecode/tree/MergeTrees.java new file mode 100644 index 0000000..8230948 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/MergeTrees.java @@ -0,0 +1,237 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-26 09:14 + * @Description: TODO 力扣617题 合并二叉树: + * 1)给你两棵二叉树: root1 和 root2 。 + * 2)想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。 + * 你需要将这两棵树合并成一棵新二叉树。 + * 合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值; + * 否则,不为 null 的节点将直接作为新二叉树的节点。 + * 3)返回合并后的二叉树。 + * @Version: 1.0 + */ +public class MergeTrees { + + @Test + public void test() { + TreeNode root = new TreeNode(1); + TreeNode TreeNode2 = new TreeNode(3); + TreeNode TreeNode3 = new TreeNode(2); + TreeNode TreeNode4 = new TreeNode(5); + + + TreeNode TreeNode5 = new TreeNode(2); + TreeNode TreeNode6 = new TreeNode(1); + TreeNode TreeNode7 = new TreeNode(3); + TreeNode TreeNode8 = new TreeNode(4); + TreeNode TreeNode9 = new TreeNode(7); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode2.setLeft(TreeNode4); + + + TreeNode5.setLeft(TreeNode6); + TreeNode5.setRight(TreeNode7); + TreeNode6.setRight(TreeNode8); + TreeNode7.setRight(TreeNode9); + + System.out.println(preorderTraversal2(mergeTrees2(root, TreeNode5))); //3 4 5 4 5 7 + } + + + /** + * 自己递归法: + * 速度击败100%,内存击败96.05% + * 本质上是深度优先算法 + * @param root1 + * @param root2 + * @return + */ + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + + if (root1 == null) { + return root2; + } else { + if (root2 != null) { + root1.val = root1.val + root2.val; + root1.left = mergeTrees(root1.left, root2.left); + root1.right = mergeTrees(root1.right, root2.right); + } + return root1; + } + + } + + + /** + * 自己迭代法:前序层序应该都可以 + * 速度击败11.89%,内存击败27.67% + * + * @param root1 + * @param root2 + * @return + */ + public TreeNode mergeTrees1(TreeNode root1, TreeNode root2) { + + if (root1 == null) { + return root2; + } + if (root2 == null) { + return root1; + } + + //存放root1节点 + Stack treeNodes1 = new Stack<>(); + //存放root2节点 + Stack treeNodes2 = new Stack<>(); + + treeNodes1.push(root1); + treeNodes2.push(root2); + + + while (!treeNodes1.empty() || !treeNodes2.empty()) { + TreeNode node1 = treeNodes1.pop(); + TreeNode node2 = treeNodes2.pop(); + + if (node2 == null) { + continue; + } + + node1.val += node2.val; + + if (node1.left != null) { + treeNodes1.push(node1.left); + treeNodes2.push(node2.left); + } else { + node1.left = node2.left; + } + + if (node1.right != null) { + treeNodes1.push(node1.right); + treeNodes2.push(node2.right); + } else { + node1.right = node2.right; + } + + + } + + return root1; + + + } + + /** + * 自己迭代法:改进版,使用一个stack + * 速度击败11.89%,内存击败81.44% + * 本质上是广度优先算法 + * @param root1 + * @param root2 + * @return + */ + public TreeNode mergeTrees2(TreeNode root1, TreeNode root2) { + + if (root1 == null) { + return root2; + } + //只要root1不等于null即可,root2等于0的逻辑在后面也可以判断了 + + + //存放root1节点 + Queue deque = new LinkedList(); + + deque.offer(root1); + deque.offer(root2); + + while (!deque.isEmpty()) { + TreeNode node1 = deque.poll(); + TreeNode node2 = deque.poll(); + + if (node2 == null) { + continue; + } + + node1.val += node2.val; + + if (node1.left != null) { + deque.offer(node1.left); + deque.offer(node2.left); + } else { + node1.left = node2.left; + } + + if (node1.right != null) { + deque.offer(node1.right); + deque.offer(node2.right); + } else { + node1.right = node2.right; + } + } + + return root1; + } + + + /** + * Morris 遍历:将空间复杂度降到O(1) + * Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。其前序遍历规则总结如下: + *

+ * 1.新建临时节点,令该节点为 root; + * 2.如果当前节点的左子节点为空,将当前节点加入答案,并遍历当前节点的右子节点; + * 3.如果当前节点的左子节点不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点: + * 如果前驱节点的右子节点为空,将前驱节点的右子节点设置为当前节点。然后将当前节点加入答案,并将前驱节点的右子节点更新为当前节点。当前节点更新为当前节点的左子节点。 + * 如果前驱节点的右子节点为当前节点,将它的右子节点重新设为空。当前节点更新为当前节点的右子节点。 + * 4.重复步骤 2 和步骤 3,直到遍历结束。 + *

+ * TODO 核心理念是如何回到root节点的: + * 1)传统方法使用stack去回到root + * 2)这种方法利用叶子结点闲置的右指针,因为回到root的前提是左边的遍历完了,那么遍历完了之前的一个节点一定是叶子节点,可以提前找到这个叶子结点,将其右指针置为root,就可以回到root了 + * + * @param root + * @return + */ + public List preorderTraversal2(TreeNode root) { + List res = new ArrayList(); + if (root == null) { + return res; + } + + TreeNode p1 = root, p2 = null; + + while (p1 != null) { + p2 = p1.left; + if (p2 != null) { + //通过这个循环找到回到root的最后一个节点,第二个条件是判断是否遍历过这个节点 + while (p2.right != null && p2.right != p1) { + p2 = p2.right; + } + //从第一个条件出来的,证明这个节点没遍历过且是回到root前的最后一个节点 + if (p2.right == null) { + res.add(p1.val); + //让下次得以回到之前遍历的节点 + p2.right = p1; + //找到了回来的节点,放心的将root左移 + p1 = p1.left; + continue; + } else { + //从第二个条件出来的,证明这个节点遍历过了,那么就把他的右边置空 + p2.right = null; + } + } else { + //左边遍历完了 + res.add(p1.val); + } + p1 = p1.right; + } + return res; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/SearchBST.java b/Leecode/src/main/java/com/markilue/leecode/tree/SearchBST.java new file mode 100644 index 0000000..11fa7cb --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/SearchBST.java @@ -0,0 +1,97 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-26 10:31 + * @Description: TODO 力扣700题 二叉搜索树中的搜索: + * 给定二叉搜索树(BST)的根节点root和一个整数值val。 + * 你需要在 BST 中找到节点值等于val的节点。 返回以该节点为根的子树。 如果节点不存在,则返回null。 + * @Version: 1.0 + */ +public class SearchBST { + + @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 TreeNode5 = new TreeNode(2); +// TreeNode TreeNode6 = new TreeNode(1); +// TreeNode TreeNode7 = new TreeNode(3); +// TreeNode TreeNode8 = new TreeNode(4); +// TreeNode TreeNode9 = new TreeNode(7); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + +// +// TreeNode5.setLeft(TreeNode6); +// TreeNode5.setRight(TreeNode7); +// TreeNode6.setRight(TreeNode8); +// TreeNode7.setRight(TreeNode9); + + System.out.println(searchBST(root, 2)); + } + + + /** + * 自己递归法: + * 速度超过100%,内存超过94.85% + * + * @param root + * @param val + * @return + */ + public TreeNode searchBST(TreeNode root, int val) { + if (root == null) { + return null; + } + if (root.val > val) { + return searchBST(root.left, val); + } else if (root.val < val) { + return searchBST(root.right, val); + } else { + return root; + } + + } + + + /** + * 自己迭代法: + * 速度超过100%,内存超过25.48% + * + * @param root + * @param val + * @return + */ + public TreeNode searchBST1(TreeNode root, int val) { + if (root == null) { + return null; + } + TreeNode node = root; + + while (node != null) { + if (node.val > val) { + node = node.left; + } else if (node.val < val) { + node = node.right; + } else { + return node; + } + } + return node; + + + } +}