diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T88_ReconstructQueue.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T88_ReconstructQueue.java new file mode 100644 index 0000000..77c27f4 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T88_ReconstructQueue.java @@ -0,0 +1,100 @@ +package com.markilue.leecode.hot100; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-04-07 10:18 + *@Description: + * TODO 力扣406 根据身高重建队列: + * 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。 + * 每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。 + * 请你重新构造并返回输入数组 people 所表示的队列。 + * 返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。 + *@Version: 1.0 + */ +public class T88_ReconstructQueue { + + @Test + public void test() { + int[][] people = {{7, 0}, {4, 4}, {7, 1}, {5, 0}, {6, 1}, {5, 2}}; +// reconstructQueue1(people); + for (int[] ints : reconstructQueue1(people)) { + System.out.println(Arrays.toString(ints)); + } + } + + + /** + * 思路:排序后使用linkedList插入 + * 速度击败26.34% 内存击败70.6% 9ms + * @param people + * @return + */ + public int[][] reconstructQueue(int[][] people) { + + Arrays.sort(people, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]; + } + }); + + LinkedList list = new LinkedList<>(); + + for (int[] person : people) { + list.add(person[1], person); + } + int[][] result = new int[people.length][]; + + + return list.toArray(new int[people.length][]); + + + } + + + /** + * 先排序后腾挪 + * 速度击败81.31% 内存击败17.3% 6ms + * @param people + * @return + */ + public int[][] reconstructQueue1(int[][] people) { + + Arrays.sort(people, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + return o1[0] == o2[0] ? o1[1] - o2[1] : o1[0] - o2[0]; + } + }); + + for (int i = people.length - 1; i >= 0; i--) { + int[] person = people[i]; + + //看看前面有几个和他相等的 + int same = 0; + for (int j = i - 1; j >= 0; j--) { + if (people[j][0] == person[0]) same++; + if (people[j][0] < person[0]) break;//小了直接break + } + //不相等,需要腾挪 + if (same != person[1]) { + int length = person[1] - same; + System.arraycopy(people, i + 1, people, i, length); + people[i + length] = person; + } + } + + + return people; + + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T89_CanPartition.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T89_CanPartition.java new file mode 100644 index 0000000..40fc5c9 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T89_CanPartition.java @@ -0,0 +1,54 @@ +package com.markilue.leecode.hot100; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-04-07 10:52 + *@Description: + * TODO 力扣416 分割等和数组: + * 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 + *@Version: 1.0 + */ +public class T89_CanPartition { + + @Test + public void test() { +// int[] nums={1,5,11,5}; + int[] nums = {1, 2, 3, 5}; + System.out.println(canPartition(nums)); + } + + /** + * 思路:本质上就是找一个总和一半的子序列 + * @param nums + * @return + */ + public boolean canPartition(int[] nums) { + + if (nums.length < 2) return false; + int sum = 0; + int max = 0; + for (int num : nums) { + sum += num; + max = Math.max(max, num); + } + + if (sum % 2 != 0) return false; + int target = sum / 2; + if (max > target) return false; + boolean[] dp = new boolean[target + 1]; + dp[0]=true; + + for (int i = 0; i < nums.length; i++) { + for (int j = dp.length - 1; j >= nums[i]; j--) { + dp[j] |= dp[j - nums[i]]; + } + } + + return dp[target]; + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T90_PathSum.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T90_PathSum.java new file mode 100644 index 0000000..9468331 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T90_PathSum.java @@ -0,0 +1,141 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.tree.TreeNode; +import com.markilue.leecode.tree.TreeUtils; +import org.junit.Test; + +import java.util.*; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-04-07 11:05 + *@Description: + * TODO 力扣437 路径总和III: + * 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 + * 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 + *@Version: 1.0 + */ +public class T90_PathSum { + + @Test + public void test() { + TreeNode root = TreeUtils.structureTree(new ArrayList<>(Arrays.asList(10, 5, -3, 3, 2, null, 11, 3, -2, null, 1)), 0); + System.out.println(pathSum(root, 8)); + } + + + /** + * 思路:可以把return的状态分为 不要之前的左 不要之前右 都不要 + * 有问题:节点的始末不一定是终点 + * @param root + * @param targetSum + * @return + */ + public int pathSum(TreeNode root, int targetSum) { + cur.add(0); + path(root, targetSum, 0); + + return sum; + } + + int sum = 0; + List cur = new ArrayList(); + + public void path(TreeNode root, int targetSum, int last) { + if (root == null) return; + int value = root.val; + for (Integer integer : cur) { + if (integer + value == targetSum) sum++; + } + + int sum = value + last; + cur.add(value); + cur.add(sum); + + path(root.left, targetSum, sum); + cur.remove(cur.size() - 1); + path(root.right, targetSum, sum); + cur.remove(cur.size() - 1); + } + + + /** + * 分别计算节点为根的,和他为路径的之和 + * 时间复杂度为O(N^2) + * 速度击败8.17% 内存击败46.54% + * @param root + * @param targetSum + * @return + */ + public int pathSum1(TreeNode root, int targetSum) { + if (root == null) { + return 0; + } + + int ret = rootSum(root, targetSum);//计算他为根的 + ret += pathSum1(root.left, targetSum);//计算不要他的 为路径的 + ret += pathSum1(root.right, targetSum); + return ret; + } + + public int rootSum(TreeNode root, int targetSum) { + int ret = 0; + + if (root == null) { + return 0; + } + int val = root.val; + if (val == targetSum) { + ret++; + } + + ret += rootSum(root.left, targetSum - val); + ret += rootSum(root.right, targetSum - val); + return ret; + } + + + /** + * 官方前缀和解法 + * 时间复杂度O(n) + * * 前缀和的递归回溯思路 + * * 从当前节点反推到根节点(反推比较好理解,正向其实也只有一条),有且仅有一条路径,因为这是一棵树 + * * 如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了 + * * 所以前缀和对于当前路径来说是唯一的,当前记录的前缀和,在回溯结束,回到本层时去除,保证其不影响其他分支的结果 + * @param root + * @param targetSum + * @return + */ + public int pathSum2(TreeNode root, int targetSum) { + Map prefix = new HashMap(); + prefix.put(0L, 1); + return dfs(root, prefix, 0, targetSum); + } + + public int dfs(TreeNode root, Map prefix, long curr, int targetSum) { + if (root == null) { + return 0; + } + + int ret = 0; + curr += root.val; + + //---核心代码 + //看看root到当前节点这条路上是否存在节点前缀和加target为currSum的路径 + //当前节点->root节点反推,有且仅有一条路径,如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了 + //currSum-target相当于找路径的起点,起点的sum+target=currSum,当前点到起点的距离就是target + + + ret = prefix.getOrDefault(curr - targetSum, 0); + prefix.put(curr, prefix.getOrDefault(curr, 0) + 1); + ret += dfs(root.left, prefix, curr, targetSum); + ret += dfs(root.right, prefix, curr, targetSum); + prefix.put(curr, prefix.getOrDefault(curr, 0) - 1); + + return ret; + } + + +}