递归回溯分治--07-组合总和Ⅱ[中等]

力扣

题目

给定一个数组 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] ]

示例 2:

  • 输入: candidates = [2,5,2,1,2], target = 5,
  • 所求解集为:[  [1,2,2],  [5] ]

思路

直观的思路仍是直接使用笔记4中的枚举法,但简单的思考就得知时间复杂度为O(2^n*n),实际上是非常差的,尤其对本题,可以使用剪枝。

例如 candidates = [10,1,2,7,6,1,5], target = 8, 很直观的就看出,凡是带有元素10的子集,都不能满足相加为8的结果,因此这部分需要剪枝。如果使用笔记4的方法,对于大样本一定会超时。

故,本题使用回溯法更佳。

在搜索回溯过程中进行剪枝操作:递归调用时,计算已选择元素的和sum,若sum>target,不再进行更深的搜索,直接返回。

答案

class Solution {
public:
	vector<vector<int>> result;
	vector<int> item;  //独立子集的记录
	set<vector<int>> res_set; //用于剔除重复结果

	vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
		sort(candidates.begin(), candidates.end());
		findResult(0, candidates,0,target);
		//将结果导入vector中
		for (set<vector<int>>::iterator it = res_set.begin(); it != res_set.end(); it++)
			result.push_back(*it);
		return result;
	}

	void findResult(int i, vector<int>& nums,int sum,int target) {
		//当元素已选完 或item中的元素和sum超过target 结束
		//大于target不再进行为剪枝
		if (i>=nums.size()||sum>target) 
			return;
		
		sum = sum + nums[i];
		item.push_back(nums[i]);
		//当item中的元素和为target且该结果未添加时
		//set自动剔除重复元素
		if (target == sum ) 
			res_set.insert(item);
		findResult(i+1, nums, sum, target);
		sum = sum - nums[i];  //回溯后 sum将nums[i]减去并从item中删除
		item.pop_back();
		findResult(i + 1, nums, sum, target);
	}
};
#include <iostream>
#include <set>
#include <vector>
#include <algorithm> //标准算法的头文件
using namespace std;

class Solution {
public:
	vector<vector<int>> result;
	vector<int> item;  //独立子集的记录
	set<vector<int>> res_set; //用于剔除重复结果

	vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
		sort(candidates.begin(), candidates.end());
		findResult(0, candidates,0,target);
		//将结果导入vector中
		for (set<vector<int>>::iterator it = res_set.begin(); it != res_set.end(); it++)
			result.push_back(*it);
		return result;
	}

	void findResult(int i, vector<int>& nums,int sum,int target) {
		//当元素已选完 或item中的元素和sum超过target 结束
		//大于target不再进行为剪枝
		if (i>=nums.size()||sum>target) 
			return;
		
		sum = sum + nums[i];
		item.push_back(nums[i]);
		//当item中的元素和为target且该结果未添加时
		//set自动剔除重复元素
		if (target == sum ) 
			res_set.insert(item);
		findResult(i+1, nums, sum, target);
		sum = sum - nums[i];  //回溯后 sum将nums[i]减去并从item中删除
		item.pop_back();
		findResult(i + 1, nums, sum, target);
	}
};


void test01()
{
	Solution solution;
	vector<int>v0 = { 10,1,2,7,6,1,5 };

	vector<vector<int>> ret = solution.combinationSum2(v0,8);
	cout << ret.size() << endl;
	//迭代器遍历
	for (vector<vector<int>>::iterator it = ret.begin(); it != ret.end(); it++) {
		for (vector<int>::iterator subit = (*it).begin(); subit != (*it).end(); subit++)
			cout << (*subit) << "\t";
		cout << endl;
	}
}

int main()
{
	test01();

	system("pause");
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值