最接近目标值的子序列和
难度:
标签:
题目描述
代码结果
运行时间: 545 ms, 内存: 147.6 MB
/*
* 思路:
* 1. 使用流来生成所有可能的子序列和。
* 2. 将数组分成两部分,分别处理。
* 3. 使用流和二分查找合并结果,找到绝对差最小的值。
*/
import java.util.*;
import java.util.stream.*;
public class Solution {
public int minAbsDifference(int[] nums, int goal) {
int n = nums.length;
List<Integer> leftSums = generateSums(Arrays.copyOfRange(nums, 0, n / 2));
List<Integer> rightSums = generateSums(Arrays.copyOfRange(nums, n / 2, n));
Collections.sort(rightSums);
return leftSums.stream()
.mapToInt(sum -> {
int remaining = goal - sum;
int idx = Collections.binarySearch(rightSums, remaining);
if (idx >= 0) {
return 0;
} else {
idx = -idx - 1;
int diff1 = idx < rightSums.size() ? Math.abs(remaining - rightSums.get(idx)) : Integer.MAX_VALUE;
int diff2 = idx > 0 ? Math.abs(remaining - rightSums.get(idx - 1)) : Integer.MAX_VALUE;
return Math.min(diff1, diff2);
}
})
.min()
.orElse(Integer.MAX_VALUE);
}
private List<Integer> generateSums(int[] nums) {
return IntStream.range(0, 1 << nums.length)
.mapToObj(mask -> IntStream.range(0, nums.length)
.filter(i -> (mask & (1 << i)) != 0)
.map(i -> nums[i])
.sum())
.collect(Collectors.toList());
}
}
解释
方法:
此题解采用了分治与双指针的策略。首先,它将输入数组分为两部分,分别计算每部分可能的子序列和,存储在两个列表中。对于计算子序列和的过程,使用了一个集合来避免重复和。每次从当前子序列和集合中取一个元素,并与当前数字相加,形成新的子序列和,更新到集合中。然后对两个结果列表排序,并使用双指针技术来找到两个列表中元素之和最接近目标值的组合。指针的移动根据当前和与目标值的比较结果进行:如果和小于目标值,则移动较小列表的指针;如果和大于目标值,则移动较大列表的指针。这种方法可以有效地缩小搜索范围,直到找到最小的绝对差。
时间复杂度:
O(2^(n/2) log(2^(n/2)))
空间复杂度:
O(2^(n/2))
代码细节讲解
🦆
在生成子序列和的过程中,使用集合而不是列表来存储和的原因是什么?这对性能有何影响?
▷🦆
题解中提到,如果和小于目标值,则移动较小列表的指针;如果和大于目标值,则移动较大列表的指针。这种指针移动策略是如何确保能快速找到最接近目标值的和的?
▷🦆
双指针技术在这种场景中为什么有效?它与单纯的遍历所有可能的子序列和有何优势?
▷🦆
题解最后的循环处理中,当指针越界(例如i>=len(res1)或j<0)时,如何处理?这种边界处理对算法的正确性和完整性有何影响?
▷