leetcode刷题记录——16组合总和

难度:中等

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 
示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]
示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]
 

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:知道用回溯法,但是有些细节拿捏不准,看到力扣题解上的这个思路比较好理解

题意
给你一个数组,里面都是不带重复的正数,再给定 target,求出所有和为 target 的组合。
元素可以重复使用,但组合不能重复,比如 [2, 2, 3] 与 [2, 3, 2] 是重复的组合。

思路在图里

leetcode刷题记录——16组合总和
×表示:当前组合和之前生成的组合重复了,比如最左边的×: 2232,与之前生成过的 2223 重复(虽然它 > target 被舍弃了)
△表示:当前的求和超出 target,不能选下去了,返回。
○表示:求和正好等于 target,加入解集,并返回。

利用约束条件剪枝
后两个约束条件很简单,设置递归结束条件:


if (sum >= target) {
  if (sum == target) {
    res.push(temp.slice()); // 加入解集
  }
  return;     // 结束当前递归
}
不产生重复组合怎么限制(剪枝)?
如上图,只要限制下一次选择的起点,是基于本次的选择,这样下一次就不会选到本次选择的同层左边的数。即通过控制 for 遍历的起点,去掉会产生重复组合的选项。


for (let i = start; i < candidates.length; i++) { // 枚举当前可选的数,从start开始
    temp.push(candidates[i]);          // 选这个数
    dfs(i, temp, sum + candidates[i]); // 基于此,继续选择,传i,下一次就不会选到i左边的数
    temp.pop();   // 撤销选择,回到选择candidates[i]之前的状态,继续尝试选同层右边的数
}
注意,子递归传了 i 而不是 i+1 ,因为元素可以重复选入集合,如果传 i+1 就不重复了。

作者:xiao_ben_zhu
链接:https://leetcode-cn.com/problems/combination-sum/solution/shou-hua-tu-jie-zu-he-zong-he-combination-sum-by-x/
来源:力扣(LeetCode)
 

JavaScript解法:

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum = function(candidates, target) {

  const res = [];

  const dfs = (start, temp, sum) => { // start是当前选择的起点索引 temp是当前的集合 sum是当前求和
    if (sum >= target) {
      if (sum == target) {
        res.push(temp.slice()); // temp的拷贝 加入解集
      }
      return;   // 结束当前递归
    }
    for (let i = start; i < candidates.length; i++) { // 枚举当前可选的数,从start开始
      temp.push(candidates[i]);          // 选这个数
      dfs(i, temp, sum + candidates[i]); // 基于此继续选择,传i,下一次就不会选到i左边的数
      temp.pop();   // 撤销选择,回到选择candidates[i]之前的状态,继续尝试选同层右边的数
    

    }
  };


  dfs(0, [], 0); // 最开始可选的数是从第0项开始的,传入一个空集合,sum也为0
  return res;
};

 

上一篇:【深度优先搜索DFS】Lintcode 135. 数字组合


下一篇:回溯:组合问题