回溯——组合总和

题目要求:

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个

这道题是经典的回溯算法应用题,核心思路是:通过递归遍历数组元素,每次选择一个数字加入组合,累加和,直到和等于目标值则记录组合;如果和超过目标值则回溯(撤销选择),继续尝试其他数字。

关键点

  1. 无重复元素,同一个数字可无限重复选取
  2. 组合去重:通过控制遍历起始索引避免重复组合(比如不回头选之前的数字)
  3. 回溯三要素:选择、递归、撤销选择

完整Java代码如下:

import java.util.ArrayList;
import java.util.List;

public class CombinationSum {

// 最终结果集合
List<List<Integer>> res = new ArrayList<>();
// 临时存储当前组合
List<Integer> path = new ArrayList<>();

public List<List<Integer>> combinationSum(int[] candidates, int target) {
    // 回溯搜索,从索引0开始
    backtrack(candidates, target, 0);
    return res;
}

/**
 * 回溯函数
 * @param candidates 候选数组
 * @param remain 剩余需要凑的目标值
 * @param start 遍历起始索引(避免重复组合)
 */
private void backtrack(int[] candidates, int remain, int start) {
    // 终止条件1:剩余值=0,找到有效组合,加入结果集
    if (remain == 0) {
        res.add(new ArrayList<>(path)); // 必须new,否则会被后续修改
        return;
    }
    // 终止条件2:剩余值<0,组合无效,直接返回
    if (remain < 0) {
        return;
    }

    // 从start开始遍历,避免重复组合(不回头选之前的数)
    for (int i = start; i < candidates.length; i++) {
        // 1. 选择:将当前数字加入组合
        path.add(candidates[i]);
        // 2. 递归:继续从i开始(可重复选当前数),剩余值减去当前数
        backtrack(candidates, remain - candidates[i], i);
        // 3. 撤销选择:回溯,移除最后加入的数字
        path.remove(path.size() - 1);
    }
}

// 测试示例
public static void main(String[] args) {
    CombinationSum solution = new CombinationSum();
    // 示例1
    int[] candidates1 = {2,3,6,7};
    int target1 = 7;
    System.out.println(solution.combinationSum(candidates1, target1));
    // 输出:[[2,2,3],[7]]

    // 示例2
    int[] candidates2 = {2,3,5};
    int target2 = 8;
    System.out.println(solution.combinationSum(candidates2, target2));
    // 输出:[[2,2,2,2],[2,3,3],[3,5]]
}

}

代码核心解释:

1. 回溯函数参数

  • remain:剩余需要凑的目标值(每次选一个数就减去它,简化判断)
  • start:遍历起始索引,保证不重复生成组合
    比如选了2之后,只从2及之后的数字选,不会回头选3再选2,避免[2,3]和[3,2]重复

2. 核心逻辑

  1. 选择:把当前数字加入临时组合path
  2. 递归:继续从当前索引i递归(允许重复选当前数字),剩余值更新为remain-candidates[i]
  3. 回溯:递归结束后,移除path最后一个元素,尝试下一个数字

3. 终止条件

  • remain = 0:组合和等于目标值,将path拷贝一份加入结果集
  • remain < 0 组合和超过目标值,直接剪支,停止当前分支
posted @ 2026-04-27 11:47  AlexXuu  阅读(4)  评论(0)    收藏  举报