DeepSeek LeetCode 1755 最接近目标值的子序列和 public int minAbsDifference(int[] nums, int goal)
这个问题可以通过将数组分成两半并枚举所有子序列和,然后排序和二分查找来高效解决,时间复杂度为 O(2^{n/2} \cdot n)。
算法思路
1. 将数组 nums 分成两部分 left 和 right,长度分别为 n/2 和 n - n/2。
2. 分别枚举两部分的所有子序列(包括空序列),得到两个和列表 sumsL 和 sumsR。
3. 对 sumsR 排序。
4. 遍历 sumsL 中的每个和 sumL,在 sumsR 中二分查找最接近 goal - sumL 的值,更新最小绝对差。
5. 返回最小绝对差。
代码实现
```java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Solution {
public int minAbsDifference(int[] nums, int goal) {
int n = nums.length;
// 分成两半
int[] left = new int[n / 2];
int[] right = new int[n - n / 2];
System.arraycopy(nums, 0, left, 0, left.length);
System.arraycopy(nums, n / 2, right, 0, right.length);
// 生成所有子序列和
List<Integer> sumsL = generateSubsetSums(left);
List<Integer> sumsR = generateSubsetSums(right);
// 排序右半部分
Collections.sort(sumsR);
int ans = Integer.MAX_VALUE;
for (int sumL : sumsL) {
int target = goal - sumL;
// 二分查找最接近 target 的值
int idx = Collections.binarySearch(sumsR, target);
if (idx >= 0) {
// 精确匹配,绝对差为 0
return 0;
}
// 插入点:-idx-1
int insertPos = -idx - 1;
// 检查插入点左边的元素
if (insertPos > 0) {
int sumR = sumsR.get(insertPos - 1);
ans = Math.min(ans, Math.abs(sumL + sumR - goal));
}
// 检查插入点右边的元素
if (insertPos < sumsR.size()) {
int sumR = sumsR.get(insertPos);
ans = Math.min(ans, Math.abs(sumL + sumR - goal));
}
}
return ans;
}
// 枚举数组所有子序列的和(包括空序列)
private List<Integer> generateSubsetSums(int[] arr) {
List<Integer> sums = new ArrayList<>();
int m = arr.length;
// 使用位掩码枚举所有子集
for (int mask = 0; mask < (1 << m); mask++) {
int sum = 0;
for (int i = 0; i < m; i++) {
if ((mask & (1 << i)) != 0) {
sum += arr[i];
}
}
sums.add(sum);
}
return sums;
}
}
```
复杂度分析
· 时间复杂度:生成子集和需要 O(2^{n/2}),排序右半部分 O(2^{n/2} \log(2^{n/2})),遍历左半部分时每次二分查找 O(\log(2^{n/2})),总体为 O(2^{n/2} \cdot n)。
· 空间复杂度:存储两个子集和列表,O(2^{n/2})。
优化建议
· 若数组长度较小(如 ≤ 20),可直接枚举所有子序列,无需分半。
· 生成子集和时可用递归或迭代优化常数,但位掩码法已足够清晰。
更多推荐

所有评论(0)