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

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

说明:

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

示例 1:

  1. Input: candidates = [2,3,6,7], target = 7
  2. Output: [[2,2,3],[7]]
  3. Explanation:
  4. 2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
  5. 7 is a candidate, and 7 = 7.
  6. These are the only two combinations.

示例 2:

  1. Input: candidates = [2,3,5], target = 8
  2. Output: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

  1. Input: candidates = [2], target = 1
  2. Output: []

示例 4:

  1. Input: candidates = [1], target = 1
  2. Output: [[1]]

示例 5:

  1. Input: candidates = [1], target = 2
  2. Output: [[1,1]]

提示:

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

思路

本题可构建一棵树,如下图所示:
39-1.png

由于每次都可以出一样的数,所以我们没必要维护bool[] visited数组了。去重是一个难点,安装普通的深度优先搜索算法,能求出所以排列,但是找组合,我们需要用到STL的容器set

我们在DFS时,传入一个set,在每次搜索到终止条件时,对当前找到的路径path进行一次排序,然后再加入到set里。set是不包含重复元素的,我们能自动完成去重任务。

最后我们再把set的内容抄到vector< vector<int> >数组里,返回出去。

初始AC代码的效率比较低的,一个原因是没有剪枝;另一个原因是我们对path反复求和,这里头包含很多重复计算。

一个改进的方法,就是把target弄成一个变化的值。在每次DFS时,只需要判断node - target是否等于0即可。改进后的AC代码效率稍微高了一点点。

代码

  1. class Solution { // 初始AC代码, 148ms
  2. public:
  3. vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
  4. vector< vector<int> > result;
  5. if( candidates.size() == 0 ) return result;
  6. if( candidates.size() == 1 ) {
  7. if( candidates[0] > target ) return result;
  8. if( candidates[0] == target ) {
  9. result.push_back( candidates );
  10. return result;
  11. }
  12. }
  13. vector<int> path;
  14. set< vector<int> > result_set;
  15. dfs(candidates, target, result_set, path, 0);
  16. for(auto it = result_set.begin(); it != result_set.end(); it++) {
  17. result.push_back( *it );
  18. }
  19. return result;
  20. }
  21. void dfs
  22. (
  23. vector<int>& candidates,
  24. int target,
  25. set< vector<int> >& result_set,
  26. vector<int>& path,
  27. int current_sum
  28. ) {
  29. // 1. Exit condition
  30. if( current_sum >= target ) {
  31. if( current_sum == target ) {
  32. vector<int> tmp = path; // it's a must
  33. sort(tmp.begin(), tmp.end());
  34. result_set.insert( tmp );
  35. }
  36. return;
  37. }
  38. // 2. Walk every node that not visited
  39. for(int i = 0; i < candidates.size(); i++) {
  40. int node = candidates[i];
  41. path.push_back( node );
  42. dfs(candidates, target, result_set, path, calculate_sum(path));
  43. path.pop_back();
  44. }
  45. }
  46. int calculate_sum(vector<int>& nums) {
  47. int sum = 0;
  48. for(int i = 0; i < nums.size(); i++) {
  49. sum += nums[i];
  50. }
  51. return sum;
  52. }
  53. };

  1. // 改进后的AC代码,76ms
  2. class Solution {
  3. public:
  4. vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
  5. vector< vector<int> > result;
  6. if( candidates.size() == 0 ) return result;
  7. if( candidates.size() == 1 ) {
  8. if( candidates[0] > target ) return result;
  9. if( candidates[0] == target ) {
  10. result.push_back( candidates );
  11. return result;
  12. }
  13. }
  14. vector<int> path;
  15. set< vector<int> > result_set;
  16. dfs(candidates, target, result_set, path);
  17. for(auto it = result_set.begin(); it != result_set.end(); it++) {
  18. result.push_back( *it );
  19. }
  20. return result;
  21. }
  22. void dfs
  23. (
  24. vector<int>& candidates,
  25. int target,
  26. set< vector<int> >& result_set,
  27. vector<int>& path
  28. ) {
  29. // 1. Exit condition
  30. if( 0 >= target ) {
  31. if( 0 == target ) {
  32. vector<int> tmp = path;
  33. sort(tmp.begin(), tmp.end());
  34. result_set.insert( tmp );
  35. }
  36. return;
  37. }
  38. // 2. Walk every node that not visited
  39. for(int i = 0; i < candidates.size(); i++) {
  40. int node = candidates[i];
  41. path.push_back( node );
  42. dfs(candidates, target-node, result_set, path);
  43. path.pop_back();
  44. }
  45. }
  46. };