题目:加起来和为目标值的组合
描述:给出一组候选数 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)。