4.24 技巧题

This commit is contained in:
zhangsan 2025-04-24 11:38:14 +08:00
parent 8855c19e89
commit 1cfbdd2b5c
7 changed files with 273 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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 数组统计该位上多少个 1countNums
* 同时统计 1 n n 个数在该位上多少个 1countRange
* 如果 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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
/**
* [0left-1] 全是 0
* [lefti-1] 全是 1
* [iright] 未知区
* [right+1n-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++;
}
}
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}