diff --git a/src/main/java/dynamic_programming/MaxSubArray.java b/src/main/java/dynamic_programming/MaxSubArray.java new file mode 100644 index 0000000..fda733e --- /dev/null +++ b/src/main/java/dynamic_programming/MaxSubArray.java @@ -0,0 +1,29 @@ +package dynamic_programming; +/** + * 题目: 53. 最大子数组和 (maxSubArray) + * 描述:给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + * 子数组是数组中的一个连续部分。 + * + 示例 1: + 输入:nums = [-2,1,-3,4,-1,2,1,-5,4] + 输出:6 + 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。 + + * 链接:https://leetcode.cn/problems/maximum-subarray/ + */ +public class MaxSubArray { + //dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]。 + public static int maxSubArray(int[] nums) { + if (nums.length == 0) { + return 0; + } + int res = nums[0]; + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]); + res = Math.max(res, dp[i]); + } + return res; + } +} diff --git a/src/main/java/dynamic_programming/MaxUncrossedLines.java b/src/main/java/dynamic_programming/MaxUncrossedLines.java new file mode 100644 index 0000000..497f0cf --- /dev/null +++ b/src/main/java/dynamic_programming/MaxUncrossedLines.java @@ -0,0 +1,40 @@ +package dynamic_programming; + +/** + * 题目: 1035. 不相交的线 (maxUncrossedLines) + * 描述:在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 + * 现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足: + * nums1[i] == nums2[j] + * 且绘制的直线不与任何其他连线(非水平线)相交。 + * 请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。 + * + * 以这种方法绘制线条,并返回可以绘制的最大连线数。 + * + 示例 1: + 输入:nums1 = [1,4,2], nums2 = [1,2,4] + 输出:2 + 解释:可以画出两条不交叉的线,如上图所示。 + 但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。 + + * 链接:https://leetcode.cn/problems/uncrossed-lines/ + */ +//转为求最长子序列 +public class MaxUncrossedLines { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int len1 = nums1.length; + int len2 = nums2.length; + int[][] dp = new int[len1 + 1][len2 + 1]; + + for (int i = 1; i <= len1; i++) { + for (int j = 1; j <= len2; j++) { + if (nums1[i - 1] == nums2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + return dp[len1][len2]; + } +} diff --git a/src/main/java/trick/FindDuplicate.java b/src/main/java/trick/FindDuplicate.java new file mode 100644 index 0000000..13238ce --- /dev/null +++ b/src/main/java/trick/FindDuplicate.java @@ -0,0 +1,84 @@ +package trick; +/** + * 题目: 287. 寻找重复数 (findDuplicate) + * 描述:给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。 + * 假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。 + * 你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。 + * + 示例 1: + 输入:nums = [1,3,4,2,2] + 输出:2 + + * 链接:https://leetcode.cn/problems/find-the-duplicate-number/ + */ +//不会 +public class FindDuplicate { + /**“按位统计”+位运算 + * 对于答案 x(重复的数),它在二进制的某一位上是 1,那么这一位上,整个数组里置 1 的次数,一定比从 1 到 n 的所有数里置 1 的次数要多(多出的部分就是重复的那一位贡献的)。 + * 所以我们可以: + * 对 0..31 每一位,遍历 nums 数组,统计该位上多少个 1:countNums。 + * 同时统计 1 到 n 这 n 个数在该位上多少个 1:countRange。 + * 如果 countNums > countRange,说明重复的数字在这一位上是 1,否则是 0。 + * 最后把所有位拼起来,就是答案。 + * @param nums + * @return + */ + public int findDuplicate(int[] nums) { + int n = nums.length - 1; // nums 中的数都在 [1..n] 里 + int duplicate = 0; + // 对 0..31 位都做一次统计 + for (int bit = 0; bit < 32; bit++) { + int mask = 1 << bit; + int countNums = 0, countRange = 0; + // 统计数组里这一位的 1 的个数 + for (int x : nums) { + if ((x & mask) != 0) countNums++; + } + // 统计 [1..n] 里这一位的 1 的个数 + for (int i = 1; i <= n; i++) { + if ((i & mask) != 0) countRange++; + } + // 如果数组中多出来,说明重复数这一位是 1 + if (countNums > countRange) { + duplicate |= mask; + } + } + return duplicate; + } + + /** + * 数值范围 + * 因为 nums 大小是 n+1,且所有元素都在 [1..n] 里,必有重复。我们把搜索范围定在 low = 1 到 high = n。 + * + * 计数判定 + * 对中点 mid = (low + high) / 2,遍历一次数组,统计有多少元素 ≤ mid,记为 cnt。 + * + * 如果 cnt > mid,说明在 [1..mid] 里「装不下」这么多数(因为最多只有 mid 个不同的数),重复值必然落在这一半区间,于是令 high = mid。 + * 否则,说明重复值在右边区间,令 low = mid + 1。 + * 结束条件 + * 当 low == high 时,区间只剩一个数,它就是答案。 + * @param nums + * @return + */ + public int findDuplicate1(int[] nums) { + int n = nums.length - 1; // 数值范围在 [1..n] + int low = 1, high = n; + while (low < high) { + int mid = low + (high - low) / 2; + // 统计 nums 中有多少个数 <= mid + int cnt = 0; + for (int x : nums) { + if (x <= mid) cnt++; + } + // 如果“数量”超过了 mid,说明重复值在 [low..mid] + if (cnt > mid) { + high = mid; + } else { + low = mid + 1; + } + } + // low == high + return low; + } + +} diff --git a/src/main/java/trick/NextPermutation.java b/src/main/java/trick/NextPermutation.java new file mode 100644 index 0000000..72e656a --- /dev/null +++ b/src/main/java/trick/NextPermutation.java @@ -0,0 +1,47 @@ +package trick; + +import java.util.Arrays; + +/** + * 题目: 31. 下一个排列 (nextPermutation) + * 描述:整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 + * + * 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 + * 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。 + * + * 例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。 + * 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。 + * 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。 + * 给你一个整数数组 nums ,找出 nums 的下一个排列。 + * + * 必须 原地 修改,只允许使用额外常数空间。 + * + 示例 1: + 输入:nums = [1,2,3] + 输出:[1,3,2] + + * 链接:https://leetcode.cn/problems/next-permutation/ + */ +public class NextPermutation { + public void nextPermutation(int[] nums) { + int n = nums.length; + // 1. 从右向左,找到第一个「前驱/枢轴」i,使得 nums[i] < nums[j](j>i) + // 这样 nums[i+1..n-1] 一定是非增的 + for (int i = n - 2; i >= 0; i--) { + // 2. 对于找到的 i,再从右向左找第一个 nums[j] > nums[i] + for (int j = n - 1; j > i; j--) { + if (nums[j] > nums[i]) { + // 3. 交换枢轴 i 和后缀中刚好比它大的那个 j + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + // 4. 将 i+1 到末尾这段降序数组,升序排列,得到下一个最小排列 + Arrays.sort(nums, i + 1, n); + return; + } + } + } + // 如果整个数组都是降序的,说明已经是最大排列了,直接变成最小排列(全升序) + Arrays.sort(nums); + } +} diff --git a/src/main/java/trick/SortColors.java b/src/main/java/trick/SortColors.java new file mode 100644 index 0000000..463394a --- /dev/null +++ b/src/main/java/trick/SortColors.java @@ -0,0 +1,40 @@ +package trick; +/** + * 题目: 75. 颜色分类 (singleNumber) + * 描述:给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 + * 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 + * 必须在不使用库内置的 sort 函数的情况下解决这个问题。 + * + 示例 1: + 输入:nums = [2,0,2,1,1,0] + 输出:[0,0,1,1,2,2] + + * 链接:https://leetcode.cn/problems/sort-colors/ + */ +public class SortColors { + void swap(int[]a,int i,int j){ + int temp=a[i]; + a[i]=a[j]; + a[j]=temp; + } + + /** + * [0……left-1] 全是 0 + * [left……i-1] 全是 1 + * [i……right] 未知区 + * [right+1……n-1] 全是 2 + * @param nums + */ + public void sortColors(int[] nums) { + int left = 0, i = 0, right = nums.length - 1; + while (i <= right) { + if (nums[i] == 0) { + swap(nums, left++, i++); + } else if (nums[i] == 2) { + swap(nums, i, right--); + } else { // nums[i] == 1 + i++; + } + } + } +} diff --git a/src/test/java/trick/FindDuplicateTest.java b/src/test/java/trick/FindDuplicateTest.java new file mode 100644 index 0000000..f331821 --- /dev/null +++ b/src/test/java/trick/FindDuplicateTest.java @@ -0,0 +1,15 @@ +package trick; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class FindDuplicateTest { + + @Test + public void findDuplicate() { + FindDuplicate findDuplicate = new FindDuplicate(); + int[]a={3,1,3,4,2}; + findDuplicate.findDuplicate(a); + } +} \ No newline at end of file diff --git a/src/test/java/trick/SortColorsTest.java b/src/test/java/trick/SortColorsTest.java new file mode 100644 index 0000000..d918f57 --- /dev/null +++ b/src/test/java/trick/SortColorsTest.java @@ -0,0 +1,18 @@ +package trick; + +import org.junit.Test; + +import java.util.Arrays; + +import static org.junit.Assert.*; + +public class SortColorsTest { + + @Test + public void sortColors() { + SortColors solution = new SortColors(); + int[]nums = {1,2,0}; + solution.sortColors(nums); + System.out.println(Arrays.toString(nums)); + } +} \ No newline at end of file