要解决在n个数中找出1个或多个数的和等于m的问题,可以使用回溯法(深度优先搜索)来枚举所有可能的子集。以下是详细的Java实现:
思路
-
排序数组:先对数组排序,便于剪枝优化(当当前和超过目标值时提前终止)。
-
回溯法:从数组起始位置开始,递归地尝试选择或不选择每个数字:
-
剪枝:
-
结果收集:所有满足条件的子集存储在结果列表中。
代码实现
import java.util.*;
public class SubsetSum {
public static List<List<Integer>> findSubsets(int[] nums, int m) {
Arrays.sort(nums); // 排序数组,便于剪枝和去重
List<List<Integer>> result = new ArrayList<>();
backtrack(nums, m, 0, new ArrayList<>(), result);
return result;
}
private static void backtrack(int[] nums, int target, int start, List<Integer> current, List<List<Integer>> result) {
if (target < 0) {
return; // 剪枝:当前和已超过目标值
}
if (target == 0) {
result.add(new ArrayList<>(current)); // 找到有效子集
return;
}
for (int i = start; i < nums.length; i++) {
// 跳过重复数字,避免重复解
if (i > start && nums[i] == nums[i - 1]) {
continue;
}
// 剪枝:后续数字更大,无需继续
if (nums[i] > target) {
break;
}
current.add(nums[i]); // 选择当前数字
backtrack(nums, target - nums[i], i + 1, current, result); // 递归处理剩余数字
current.remove(current.size() - 1); // 回溯:撤销选择
}
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 1}; // 示例数组
int m = 5; // 目标和
List<List<Integer>> subsets = findSubsets(nums, m);
for (List<Integer> subset : subsets) {
System.out.println(subset);
}
// 输出示例: [1, 4], [2, 3], [1, 1, 3]
}
}
示例说明
-
输入:数组 [1, 2, 3, 4, 1]
,目标值 m = 5
。
-
输出:
-
[1, 4]
(1 + 4 = 5)
-
[2, 3]
(2 + 3 = 5)
-
[1, 1, 3]
(1 + 1 + 3 = 5)
关键点
此方法适用于数组元素均为正数的情况。若包含负数,需移除剪枝逻辑(因负数可能使和变小),但基本回溯框架仍适用。