leetcode之Combination Sum && Combination Sum II

Combination Sum原题如下:

Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:

  • All numbers (including target) will be positive integers.
  • Elements in a combination (a1a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
  • The solution set must not contain duplicate combinations.

For example, given candidate set 2,3,6,7 and target 7
A solution set is: 
[7] 
[2, 2, 3] 

这道题是典型的的回溯算法题,因为最终组合要求升序,所以首先将vector中元素排序,然后从第一个元素开始进行深度遍历,为了保证最终组合不能重复,并且由于同一元素可以出现多次,所以需要从当前加入的节点开始向后遍历,又因为原数组可能存在重复元素,所以在开始遍历时需要判断该元素是否已被访问过。

class Solution {
public:
    vector<vector<int> > combinationSum(vector<int> &candidates, int target) {     
		vector<vector<int>>vv;
		sort(candidates.begin(),candidates.end());
		vector<int>v;
		combination(vv,v,candidates,0,target);
		return vv;

    }
	void combination(vector<vector<int>>&vv,vector<int>v,vector<int>cand,int start,int target){
        if(target < 0)
			return;
		if(target == 0){
			vv.push_back(v);
			return;
		}
		for(int i = start; i < cand.size(); i++){
		    if(i > 0 && cand[i] == cand[i - 1])
				continue;
			v.push_back(cand[i]);
			combination(vv,v,cand,i,target - cand[i]);
			v.pop_back();
		}
	}
};

之前确实遇到了好多这种题目,其做法都是相似的,回溯算法的共同特点就是逐步探索,当走不通时回退一步继续选择其它路径,回溯算法在路径选择问题中确实强大,但由于该问题是NP困难的,所以其复杂度是指数级别的。

Combination Sum II 的原题如下:

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:

  • All numbers (including target) will be positive integers.
  • Elements in a combination (a1a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
  • The solution set must not contain duplicate combinations.

For example, given candidate set 10,1,2,7,6,1,5 and target 8
A solution set is: 
[1, 7] 
[1, 2, 5] 
[2, 6] 
[1, 1, 6] 

这一题乍一看比上一题还简单,因为这里边每个元素最多用一次,但实际情况是,如果数组中没有重复元素确实比第一题还要简单,但重复元素的存在大大增加了此题的难度,因为此时不能像上述题目那样简单的跳过重复元素(因为重复元素也可以在组合中重复出现),在网上看到别人的代码,自己测试通过:

class Solution {
public:
    vector<vector<int> > combinationSum2(vector<int> &num, int target) {
        vector<vector<int>>vv;
		sort(num.begin(),num.end());
		vector<int>v;
		combination(vv,v,num,0,target);
		return vv;
    }
	void combination(vector<vector<int>>&vv,vector<int>v,vector<int>num,int start,int target){
		if(target == 0){			
			vv.push_back(v);
			return;
		}
		if(target < 0  || start >= num.size())
			return;		
		for(int i = start; i < num.size(); i++){				
			v.push_back(num[i]);
			combination(vv,v,num,i + 1,target - num[i]);
			v.pop_back();
			while(i < num.size() - 1 && num[i] == num[i + 1])
				i++;
		}
	    
	}
};

刚开始看到还有点儿不大理解,后来仔细推理了以下才恍然大悟,代码中一次for循环相当于以i节点为初始节点(该节点一定在组合中)寻找可能的组合,而重复元素只需在第一次出现时作为初始节点寻找即可,后边的重复元素不必以初始节点出现在组合中,所以需要在for循环后加以控制。

上述两题的关键都是去除重复元素,第一题是在递归前去重,其原因是凡是重复元素都不必在遍历中出现(可以直接略过),因此去重时的比较是前向比较,而第二题是在递归后去重,其原因是重复元素只需被递归遍历一次(即以第一个重复元素为初始节点就够了),只是在后续遍历中需要跳过,因此去重时的比较是后向比较。对回溯法的理解还需要深入。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值