[路飞]_39.组合总和

102 阅读2分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

39. 组合总和

题目

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例1

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例2

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

题解

回溯 + 剪枝

按照示例1给出的数据 candidates=[2,3,6,7],target=7candidates = [2,3,6,7], target = 7

graph TD
0[7] --2--> 1[5]
0[7] --3--> 2[4]
0[7] --6--> 3[1]
0[7] --7--> 4[0]

1[5]--2--> 5[3]
1[5]--3--> 6[2]
1[5]--6--> 7[-1]
1[5]--7--> 8[-2]
2[4]--2--> 9[2]
2[4]--3--> 10[1]
2[4]--6--> 11[-1]
2[4]--7--> 12[-3]
3[1]--2--> 13[-1]
3[1]--3--> 14[-2]
3[1]--6--> 15[-5]
3[1]--7--> 16[-6]


5[3] --2--> 17[1]
5[3] --3--> 18[0]
5[3] --6--> 19[-3]
5[3] --7--> 20[-4]

6[2] --2--> 21[0]
6[2] --3--> 22[-1]
6[2] --6--> 23[-4]
6[2] --7--> 24[-5]

9[2] --2--> 25[0]
9[2] --3--> 26[-1]
9[2] --6--> 27[-4]
9[2] --7--> 28[-5]
 
10[1] --2--> 29[-1]
10[1] --3--> 30[-2]
10[1] --6--> 31[-5]
10[1] --7--> 32[-6]

图没画完,是这个意思。结合上图分析,枚举数组,将目标值 - 数组元素 作为下一次枚举数组的目标值;这很明明显要用到递归了呀

通过深度优先遍历,用目标值减去枚举到的数组元素,直到目标值为 00 或者数组枚举到最后一位;停止遍历;并将目标值为 00 上的路径元素记录到结果数组中。

剪枝

依然是上图分析,在递归过程中每次都从左到右枚举数组可能会出现重复,比如图中就出现了[2,2,3],[2,3,2][2,2,3],[2,3,2]这两个重复路径,所以为了防止重复计算,可以先讲数组排序,然后

graph TD
0[7] --2--> 1[5]
0[7] --3--> 2[4]
0[7] --6--> 3[1]
0[7] --7--> 4[0]


1[5] --2--> 5[3]
1[5] --3--> 6[2]
1[5] --6--> 7[-1]
1[5] --7--> 8[-2]

5[3] --2--> 9[1]
5[3] --3--> 10[0]
5[3] --6--> 11[-3]
5[3] --7--> 12[-4]

9[1] --2--> 13[-1]
9[1] --3--> 14[-2]
9[1] --6--> 15[-5]
9[1] --7--> 16[-6]


6[2] --3--> 17[-1]
6[2] --6--> 18[-4]
6[2] --7--> 19[-5]

2[4] --3--> 20[1]
2[4] --6--> 21[-2]
2[4] --7--> 22[-3]

20[1] --3--> 23[-2]
20[1] --6--> 24[-5]
20[1] --7--> 25[-6]


3[1] --6--> 27[-5]
3[1] --7--> 28[-6]

这个剪枝图画完了。剪枝需要将数组排序,记录当前枚举位置,这样就在枚举数组的时候就不用从数组最左侧开始,降低了重复分支;

完整代码

var combinationSum = function (candidates, target) {
  candidates.sort((a, b) => a - b)
  let result = []
  const len = candidates.length
  dfs([], target, 0)
  return result
  function dfs(path, t, idx) {
    if (t === 0) {
      const a = [].concat(path)
      result.push(a)
      return
    }
    for (let i = idx; i < len; i++) {
      const c = candidates[i]
      path.push(c)
      t - c >= 0 && dfs(path, t - c, i)
      path.pop()
    }
  }
}