leetcode 40 组合数之和II(递归树结构的剪枝)

给定一个数组 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]]
解题思路:
如果该题采用递归的方法,即leetcode第90的解法,代码如下:
具体可参考leetcode第90题的注释

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        vector<int> item;
        set<vector<int>> res_set;    //去重使用的集合  
        sort(candidates.begin(),candidates.end());    //对nums数组进行排序
        result.push_back(item);
        generate(0, candidates, result, item, res_set);
        vector<vector<int>> target_result;  //储存最终结果
        for(int i = 0; i < result.size(); i++){
            int sum = 0;
            for(int j = 0; j < result[i].size(); j++){
                sum += result[i][j]; //计算个子集的和
            }
            if(sum == target){
                target_result.push_back(result[i]);
            }
        }
        return target_result;
    }
private:
    void generate(int i, vector<int>& candidates, vector<vector<int>> &result, vector<int>& item, set<vector<int>>& res_set){
        if(i >= candidates.size()){
            return;
        }
        item.push_back(candidates[i]);
        if(res_set.find(item) == res_set.end()){ //如果res_set集合中,无法找到item
            result.push_back(item);   //将item放入result数组中
            res_set.insert(item);   //将item放入去重集合res_set中
        }
        generate(i+1, candidates, result, item, res_set);
        item.pop_back();
        generate(i+1, candidates, result, item, res_set);
    }
};

但该解法在运行的时候,出现了错误原因是时间超出了限制

最后执行的输入:[14,6,25,9,30,20,33,34,28,30,16,12,31,9,9,12,34,16,25,32,8,7,30,12,33,20,21,29,24,17,27,34,11,17,30,6,32,21,27,17,16,8,24,12,12,28,11,33,10,32,22,13,34,18,12]
27

无论是回溯法或者位运算,整体时间复杂度O(2^n)
该操作应该利用到target = 8的条件,在搜索回溯过程中进行剪枝操作:
递归调用时,计算已经选择的和sum,若sum > target,不再进行更深的搜索,直接返回
例如:
nums[] = [10,1,2,7,6,1,5]
target = 8;
代码如下,可以对比上述代码,理解剪枝操作

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        vector<int> item;
        set<vector<int>> res_set;    //去重使用的集合  
        sort(candidates.begin(),candidates.end());    //对nums数组进行排序
        //result.push_back(item);
        generate(0, candidates, result, item, res_set, 0, target);
        //vector<vector<int>> target_result;  //储存最终结果
        //for(int i = 0; i < result.size(); i++){
        //    int sum = 0;
        //    for(int j = 0; j < result[i].size(); j++){
        //        sum += result[i][j]; //计算个子集的和
        //    }
        //    if(sum == target){
        //        target_result.push_back(result[i]);
        //    }
        //}
        return result;
    }
private:
    void generate(int i, vector<int>& candidates, vector<vector<int>> &result, vector<int>& item, set<vector<int>>& res_set, int sum, int target){
        if(i >= candidates.size() || sum > target){
            return;
        }
        sum += candidates[i];   //sum为当前子集item中的元素和
        item.push_back(candidates[i]);
        if(target == sum && res_set.find(item) == res_set.end()){ //如果res_set集合中,无法找到item
            result.push_back(item);   //将item放入result数组中
            res_set.insert(item);   //将item放入去重集合res_set中
        }
        generate(i+1, candidates, result, item, res_set, sum, target);
        sum -= candidates[i];  //回溯后,sum将nums[i]减去并从item中删去
        item.pop_back();
        generate(i+1, candidates, result, item, res_set, sum, target);
    }
};

测试函数如下:

int main()
{
	vector<int> nums;
	nums.push_back(10);
	nums.push_back(1);
	nums.push_back(2);
	nums.push_back(7);
	nums.push_back(6);
	nums.push_back(1);
	nums.push_back(5);
	vector<vector<int>> result;   //最终数组
	Solution solve;
	result = solve.combinationSum2(nums, 8);
	for (int i = 0; i < result.size(); i++){
		if (result[i].size() == 0) {
			printf("[]");
		}
		for (int j = 0; j < result[i].size(); j++) {
			printf("[%d]", result[i][j]);
		}
		printf("\n");
	}
	return 0;
}

结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值