题目:加起来和为目标值的组合

描述:给出一组候选数 C和一个目标数 T,找出候选数中起来和等于 T的所有组合。 C中的每个数字在一个组合中只能使用一次。

注意:题目中所有的数字(包括目标数 T)都是正整数,组合中的数字(a1,a2,…,ak)要按非递增排序(a1≤a2≤…≤ak).

结果中不能包含重复的组合,组合之间的排序按照索引从小到大依次比较,小的排在前面,如果索引相同的情况下数值相同,则比较下一个索引。

示例1输入:[100,10,20,70,60,10,50],80

返回值:[[10,10,60],[10,20,50],[10,70],[20,60]]

说明:给定的候选数集是[100,10,20,70,60,10,50],目标数是80

解法一:

思路分析:在通读题目的同时,我们可以采用回溯法来解决问题,该题目的实际含义是将集合当中的所有数字相加,当等于target目标数字的时候,返回即可,首先进行排序,然后定义一个最终存储的容器ans,定义一个暂存容器res,将符合条件的元素添加到容器当中,当相加等于target的时候,表示可以返回,将res的值保存后,存入ans容器中即可,其次还要定义一个当前变量cur记录存储的过程值,定义一个指针变量循环执行集合中的元素,主要分为三种情况,分别是:

1、当cur + num[i] > target,表示没有必要继续执行了,后面的所有值均大于target,停止这轮循环,执行下一轮循环。

2、当i > m && num[i-1] == num[i],题目要求每个数字在一个组合中只能使用一次,当违反条件,则直接执行下一轮循环。

3、进行具体判断。

实例分析:候选数集是[100,10,20,70,60,10,50],目标数是80

第一轮:

数组

10

10

20

50

60

70

100

i

将该数字存入res容器当中,cur = 10,递归,target = 70


数组

10

10

20

50

60

70

100

i

递归数

将该数字存入res容器当中,cur = 20,递归,target = 60


数组

10

10

20

50

60

70

100

i

递归数

将该数字存入res容器当中,cur = 40,递归,target = 40,随后循环发现均不符合条件,将数字20从容器中排出


数组

10

10

20

50

60

70

100

i

递归数

将该数字存入res容器当中,cur = 70,递归,target = 10,随后循环发现均不符合条件,将数字50从容器中排出



数组

10

10

20

50

60

70

100

i

递归数

符合条件,值刚好等于80,将该暂存容器中的数字放入ans容器中,继续执行。

……

以下就不进行分析了,读者可以自己进行分析运算。

具体C++代码为:


class Solution {
    
private:
    vector ans;
    vector res;//暂存
public:
    void DFS(vector num , int target , int m , int cur){//cur为当前变量,m为循环变量
        if(cur == target){//如果计算的目标数等于的话,直接添加进去
            ans.push_back(res);
            return;
        }
        else{
            for(int i = m ;i < num.size(); i++){ if(cur + num[i] > target)
                    continue;
                if(i > m && num[i-1] == num[i])
                    continue;
                else{
                    res.push_back(num[i]);//加入到暂存容器当中
                    DFS(num, target, i+1, cur + num[i]);//循环进行判断,当发现cur的值相等的时候,直接输出即可
                    res.pop_back();//出一个数继续判断
                }
            }
        }
    }
    
    
    vector combinationSum2(vector&num, int target) {
        sort(num.begin(), num.end());//题目要求按顺序排序,所以先将所有元素排序完整
        DFS(num, target, 0, 0);
        return ans;
    }
};


该算法相当于将所有元素遍历一次后,在遍历的过程中采用回溯的方法进行值的具体判断,故其时间复杂度为O(N^2),空间复杂度为O(N)。


解法二(与解法一差不多,采用java中的ArrayList对象分析):

思路分析:首先还是将数组进行排序,方便后序分析,把每一个元素作为起始数字,运用target进行循环判断,与元素后面的数字进行递归判断,最终返回结果值。

具体java代码为:


import java.util.Arrays;
import java.util.ArrayList;
 
class Solution {
    public ArrayList combinationSum2(int[] num, int target) {
        Arrays.sort(num);//排序
        ArrayList res = new ArrayList<>();//最终存储结果的对象
        DFS(num,target,0,res,new ArrayList<>());
        return res;
    }
 
    public void DFS(int[] nums, int target, int index, ArrayList res, ArrayListli){
        if(target < 0){ return ; } if(target == 0){ res.add(new ArrayList<>(li));
            return;
        }
        for(int i = index; i < nums.length; i++){ if(i > index && nums[i-1] == nums[i]){//去除重复
                continue;
            }
            li.add(nums[i]);//把当前元素加入
            DFS(nums,target-nums[i],i+1,res,li);//回溯
            li.remove(li.size()-1);//恢复
        }
    }
}


时间复杂度同上,也为O(N^2)。