Skip to content

组合总和 II-40 #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sl1673495 opened this issue Jun 12, 2020 · 3 comments
Open

组合总和 II-40 #73

sl1673495 opened this issue Jun 12, 2020 · 3 comments
Labels
待复习 看题解或者做出来很艰难的,需要回顾。 递归与回溯

Comments

@sl1673495
Copy link
Owner

sl1673495 commented Jun 12, 2020

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

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]
示例 2:

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

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

思路

组合总和-39 思路类似,只不过由于不需要考虑同一个元素重复使用的情况,每次的递归的 start 起点应该是 prevStart + 1

由于数组中可能出现多个相同的元素,他们有可能会生成相同的解,比如 [1, 1, 7] 去凑 8 的时候,可能会用下标为 0 的 1 和 7 去凑 8,也可能用下标为 1 的 1 和 7 去凑 8。

所以在把解放入数组中之前,需要先通过一个唯一的 key 去判断这个解是否生成过,但是考虑到 [1, 2, 1, 2, 7] 这种情况去凑 10,可能会生成 [1, 2, 7][2, 1, 7] 这样顺序不同但是结果相同的解,这是不符合题目要求的。

所以一个简单的方法就是,先把数组排序后再求解,这样就不会出现顺序不同的相同解了,此时只需要做简单的数组拼接即可生成 key [1, 2, 7] -> "1 ~ 2 ~ 7"

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
let combinationSum2 = function (candidates, target) {
  let res = []

  if (!candidates.length) {
    return res
  }

  candidates.sort()

  let used = {}

  let helper = (start, prevSum, prevArr) => {
    // 由于全是正整数 所以一旦和大于目标值了 直接结束本次递归即可。
    if (prevSum > target) {
      return
    }
    // 目标值达成
    if (prevSum === target) {
      let key = genKey(prevArr)
      if (!used[key]) {
        res.push(prevArr)
        used[key] = true
      }
      return
    }

    for (let i = start; i < candidates.length; i++) {
      // 这里还是继续从start本身开始 因为多个重复值是允许的
      let cur = candidates[i]
      let sum = prevSum + cur
      let arr = prevArr.concat(cur)
      helper(i + 1, sum, arr)
    }
  }

  helper(0, 0, [])

  return res
}

let genKey = (arr) => arr.join("~")
@sl1673495 sl1673495 added 待复习 看题解或者做出来很艰难的,需要回顾。 递归与回溯 labels Jun 12, 2020
@sl1673495 sl1673495 changed the title 组合总和-40 组合总和 II-40 Jun 13, 2020
@cwjbjy
Copy link

cwjbjy commented Jul 21, 2022

这个解法,数据一长,力扣就报超时了

@cwjbjy
Copy link

cwjbjy commented Jul 21, 2022

var combinationSum2 = function (candidates, target) {
    candidates.sort();
    let res = [];
    function dfs(start, prevSum, prev) {
        if (prevSum > target) return;
        if (prevSum == target) {
            res.push(prev);
            return;
        }
        let used = {};
        for (let i = start, len = candidates.length; i < len; i++) {
            let cur = candidates[i];
            if (used[cur]) continue;
            used[cur] = true;
            dfs(i + 1, prevSum + cur, prev.concat(cur));
        }
    }
    dfs(0, 0, []);
    return res;
};

@sl1673495
Copy link
Owner Author

sl1673495 commented Mar 16, 2024

更好的解法:

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum2 = function (candidates, target) {
    let res = []
    let path = []
    let total = 0
    candidates.sort((a, b) => a - b)
    let used = new Array(candidates.length).fill(false)
    let helper = (start) => {
        if (total > target) return
        if (total === target) {
            res.push([...path])
            return
        }
        for (let i = start; i < candidates.length; i++) {
            if (used[i - 1] === false && candidates[i - 1] === candidates[i]) continue
            used[i] = true
            total += candidates[i]
            path.push(candidates[i])
            helper(i + 1)
            path.pop()
            used[i] = false
            total -= candidates[i]
        }
    }
    helper(0)
    return res
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
待复习 看题解或者做出来很艰难的,需要回顾。 递归与回溯
Projects
None yet
Development

No branches or pull requests

2 participants