【力扣-回溯】4、组合总和II(40)

159 阅读2分钟

「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

40. 组合总和 II

题目描述

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

注意: 解集不能包含重复的组合。   

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

解析

与上面的题不同,本题中限制数组中的每个数字只能使用1次,所以需要进行去重处理。

组合问题可以抽象为树结构
    - 对于树:
        横向为一层,即横着所有元素在一个树层
        竖向为一个分支,即竖着的所有元素在一个树枝
    需要判断的是:
        相同的元素是在一层中使用的还是在一个数值上使用的        
        在同一个树枝上是可以的,但是在同一树层的话不行
难点: 
    去重处理(去除重复的组合)
    可以使用set来去重,但是容易超时
    所以需要在搜索的过程中对同一层使用过的元素进行去重

回溯法三部曲
    1、确定递归函数的参数与返回值
        相对于上一题,本题需要多添加一个bool类型的数组used来判断同一层中的元素是否使用过
    2、递归终止条件
        sum == target
        sum > target
    3、单层搜索的逻辑
        这里需要进行去重处理,判断相同的元素是否在同一层中
        对于在同一个树枝上的相同元素不进行去重
        如果:
            candidates[i] == candidates[i-1] , 并且 used[i-1] == false ,
            则说明前一个树枝已经使用过了 candidates[i-1] ,也就是同一层已经使用过了元素candidates[i-1]
            此时需要结束本次循环,进行下一个循环(continue)
    

代码

class Solution
{
public:
    vector<vector<int>> combinationSum2(vector<int> &candidates, int target)
    {
        result.clear();
        path.clear();
        // used数组元素全部初始化为false
        vector<bool> used(candidates.size(), false);
        //排序
        sort(candidates.begin(), candidates.end());
        backtracking(candidates, target, 0, 0, used);
        return result;
    }

private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int> &candidates, int target, int sum, int startIndex, vector<bool> &used)
    {

        // 进行剪枝操作后,下面的代码可以省略
        // if(sum>target){
        //     return;
        // }
        if (sum == target)
        {
            result.push_back(path);
        }

        // for循环中进行剪枝操作
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++)
        {
            used[i - 1] == true;  // 说明同一个树枝 candidates[i-1]使用过
            used[i - 1] == false; // 说明同一个树层 candidates[i-1]使用过
            // 对同一层中使用过的元素去重
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false)
            {
                // 同一层中已经使用过了candidates[i]元素,所以跳过
                continue;
            }

            sum += candidates[i];
            path.push_back(candidates[i]);
            used[i] = true;
            backtracking(candidates, target, sum, i + 1, used);
            used[i] = false;
            sum -= candidates[i];
            path.pop_back();
        }
    }
};