要在n个数里找1到多个数的和为m,你会怎么写?

要解决在n个数中找出1个或多个数的和等于m的问题,可以使用回溯法(深度优先搜索)来枚举所有可能的子集。以下是详细的Java实现:

思路

  1. 排序数组:先对数组排序,便于剪枝优化(当当前和超过目标值时提前终止)。

  2. 回溯法:从数组起始位置开始,递归地尝试选择或不选择每个数字:

    • 选择当前数字:将其加入当前子集,并递归处理剩余数字。

    • 不选择当前数字:跳过当前数字,继续处理后续数字。

  3. 剪枝:

    • 若当前和等于目标值,记录有效子集。

    • 若当前和超过目标值,提前返回(因数组已排序,后续数字只会更大)。

    • 跳过重复数字,避免生成重复解(需在排序后处理)。

  4. 结果收集:所有满足条件的子集存储在结果列表中。

代码实现

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)

关键点

  • 排序:确保剪枝有效(提前终止无效路径)。

  • 去重:跳过相同数字,避免生成重复子集。

  • 回溯:递归探索所有可能路径,通过撤销选择回溯到上一步。

  • 时间复杂度:最坏情况为 O(2n)O(2n)(枚举所有子集),但剪枝能显著优化实际性能。

此方法适用于数组元素均为正数的情况。若包含负数,需移除剪枝逻辑(因负数可能使和变小),但基本回溯框架仍适用。

posted @ 2025-07-10 12:23  飘来荡去evo  阅读(17)  评论(0)    收藏  举报