关于回溯法:组合总和-II
组合总和 II题目介绍给定一个候选人编号的汇合 candidates 和一个指标数 target ,找出 candidates 中所有能够使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能应用 一次 。 留神:解集不能蕴含反复的组合。 示例: 输出: candidates = [10,1,2,7,6,1,5], target = 8,输入:[[1,1,6],[1,2,5],[1,7],[2,6]]示例: 输出: candidates = [2,5,2,1,2], target = 5,输入:[[1,2,2],[5]]问题剖析办法一在这道问题当中咱们依然是从一组数据当中取出数据进行组合,而后失去指定的和,然而与后面的组合总和不同的是,在这个问题当中咱们可能遇到反复的数字而且每个数字只可能应用一次。这就给咱们减少了很大的艰难,因为如果存在雷同的数据的话咱们就又可能产生数据雷同的组合,比方在第二个例子当中咱们产生的后果[1, 2, 2]其中的2就可能来自candidates当中不同地位的2,能够是第一个,能够是第三个,也能够是最初一个2。然而在咱们的最终答案当中是不容许存在反复的组合的。当然咱们能够依照失常的形式遍历,而后将失去的复合要求的后果退出到一个哈希表当中,对失去的后果进行去重解决。然而这样咱们的工夫和空间开销都会加大很多。 在这个问题当中为了防止产生反复的汇合,咱们能够首先将这些数据进行排序,而后进行遍历,咱们拿一个数据来进行举例子:[1, 2 1],当初咱们将这个数据进行排序失去的后果为:[1, 1, 2],那么遍历的树结构如下: 上图示意[1, 1, 2]的遍历树,每一个数据都有选和不选两种状况,依据这种剖析形式能够结构下面的解树,咱们对下面的树进行剖析咱们能够晓得,在下面的树当中有一部分子树是有反复的(反复的子树那么咱们就回产生反复的后果,因而咱们要删除反复的分支,也就是不进行递归求解),如下图所示: 咱们当初来剖析一下下面图中产生重复子树的起因,在一层当中选1的到第二层不选1的子树和第一层不选1而第二层选1的树产生了反复,因而咱们能够在第一层不选1第二层选1的子树当中进行递归。 依据下面的例子咱们能够总结进去,在同一层当中,如果前面的值等于他后面一个值的话,咱们就能够不去生成“抉择”这个分支的子树,因为在他的后面曾经生成了一颗截然不同的子树了。 当初咱们的问题是如何确定和上一个遍历的节点是在同一层下面。咱们能够应用一个used数组进行确定,当咱们应用一个数据之后咱们将对应下标的used数组的值设置为true,当递归实现进行回溯的时候在将对应地位的used值设置为false,因而当咱们遍历一个数据的时候如果他后面的一个数据的used值是false的话,那么这个节点就和后面的一个节点在同一层下面。 依据下面的剖析咱们能够写出如下的代码: class Solution { vector<vector<int>> ans; vector<int> path;public: vector<vector<int>> combinationSum2(vector<int>& candidates, int target) { sort(candidates.begin(), candidates.end()); vector<bool> used(candidates.size(), false); backtrace(candidates, target, 0, 0, used); return ans; } void backtrace(vector<int>& candidates, int target, int curIdx, int curSum, vector<bool>& used) { if (curSum == target) { // 满足条件则保留后果而后返回 ans.push_back(path); return; } else if (curSum > target || curIdx >= candidates.size()) { return; } if (curIdx == 0) { // 抉择分支 path.push_back(candidates[curIdx]); used[curIdx] = true; backtrace(candidates, target, curIdx + 1, curSum + candidates[curIdx], used); // 在这里进行回溯 path.pop_back(); used[curIdx] = false; // 不抉择分支 backtrace(candidates, target, curIdx + 1, curSum, used); }else { if (used[curIdx - 1] == false && candidates[curIdx - 1] == candidates[curIdx]) { // 在这里进行判断是否在同一层,如果在同一层并且值相等的话 那就不须要进行抉择了 只须要走不抉择的分支及即可 backtrace(candidates, target, curIdx + 1, curSum, used); }else{ // 抉择分支 path.push_back(candidates[curIdx]); used[curIdx] = true; backtrace(candidates, target, curIdx + 1, curSum + candidates[curIdx], used); // 在这里进行回溯 path.pop_back(); used[curIdx] = false; // 不抉择分支 backtrace(candidates, target, curIdx + 1, curSum, used); } } }};办法二在回溯算法当中咱们个别有两种抉择状况,这一点咱们在后面组合问题当中曾经介绍过了,一种办法是用抉择和不抉择去生成解树,这样咱们将生成一颗二叉树的解树,另外一种是多叉树,上面咱们来看一下后者的解树: ...