写在前面
坚持每天刷1-2道leetcode题目,保持手感,为以后找工作打基础。今天遇到了一道leetcode题目,觉得其中的一个去重的思路十分巧妙,十分值得学习,所以记录下来。
题目描述
给定一个数组 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]
]
通过的代码
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
vector<int> ans;
dfs(candidates,ans,target,0);
return res;
}
void dfs(vector<int>& candidates, vector<int>& ans,int target,int start)
{
if(target<=0)
{
if(target==0) res.push_back(ans);
return ;
}
for(int i=start;i<candidates.size();i++)
{
if(i>start&&candidates[i]==candidates[i-1])
{
continue;
}
ans.push_back(candidates[i]);
dfs(candidates,ans,target-candidates[i],i+1);
ans.pop_back();
}
}
};
解题思路
首先,这道题一看就是回溯的题目,用dfs可以很快解决,需要注意的是要学会剪枝,if(target<=0)就是用来剪枝用的,如果当前结点再往下生长已经找不到解了,就不需要继续往下了,直接return。感觉dfs的题目虽然有模板,按照模板比较容易把代码写出来,但是一定要学会剪枝,不然就可能会超时。
这个题目的重头戏还不是在dfs这里,因为dfs部分都是很常规的模板做法,重头戏在于去重,因为题目给的candidates数组中是有重复元素的,如果不去重,对于题目给的candidates=[10,1,2,7,6,1,5], target = 8,就会得到[1, 7], [1, 2, 5], [2, 6], [1, 1, 6],[2,1,5],[7,1]这种结果,很明显,其中[1,2,5]和[2,1,5]重复了,不符合题目返回的要求。
去重的关键句子是if(i>start&&candidates[i]==candidates[i-1]) continue;
还是以题目给的例子candidates=[10,1,2,7,6,1,5], target = 8为例进行说明:
(1)首先得到排序后的结果:[1,1,2,5,6,7,10]
(2) dfs(candidates,ans,target,0)执行的时候,此时的start=0,for循环执行到i=start的时候,可以将1压入ans这个vector中,然后继续往下执行 dfs(candidates,ans,target,1)。但是当for循环执行到i=start+1也就是i=1的时候,此时满足i>start并且candidates[i]==candidates[i-1],那么就应该直接continue,跳过i=1,直接到i=2。其实也是可以理解的,candidates[0]已经尝试过了,也已经得到了可行解,此时如果再尝试candidates[1],就会有一部分解和前面candidates[0]尝试后得到的可行解重复,就不符合题目要求了。
1(0) 1(1) 1(0)
/ / /
2(2) 2(2) 1(1)
/ / /
5(3) 5(3) 6(3)
(1) (2) (3)
如上图所示,为了描述清楚,我把元素对应的下标也附在图上了,图中的()前面是元素值,()里面是元素对应的下标,if(i>start&&candidates[i]==candidates[i-1]) continue;语句可以把(2)这种情况去掉,因为在i=1的时候,此时candidates[1]==candidates[0]满足,并且都为1,那么(2)这种情况就不需要再继续往下了,因为继续往下会得到和(1)完全相同的解(2)。而(3)这种情况其实是在选中了第一个元素1的时候,往下生长得到的另一个可行解,因为选中了candidates[0]之后,进入到dfs(candidates,ans,target,1),此时的start就是为1了,重新开始新的去重。