leetcode题解之40. 组合总和 II
Posted 刷题之路1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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] ]
这道题与上一问的区别在于:
- 第 39 题:candidates 中的数字可以无限制重复被选取。
- 第 40 题:candidates 中的每个数字在每个组合中只能使用一次。
编码的不同在于下一层递归的起始索引不一样。
- 第 39 题:还从候选数组的当前索引值开始。
- 第 40 题:从候选数组的当前索引值的下一位开始。
相同之处:解集不能包含重复的组合。
为了使得解集不包含重复的组合。我们想一想,如何去掉一个数组中重复的元素,除了使用哈希表以外,我们还可以先对数组升序排序,重复的元素一定不是排好序以后的第 1 个元素和相同元素的第 1 个元素。根据这个思想,我们先对数组升序排序是有必要的。候选数组有序,对于在递归树中发现重复分支,进而“剪枝”也是有效的。
思路分析:
这道题其实比上一问更简单,思路是:
以 target 为根结点,依次减去数组中的数字,直到小于 或者等于 ,把等于 的结果记录到结果集中。
当然你也可以以 为根结点,依次加上数组中的数字,直到大于 target 或者等于 target,把等于 target 的结果记录到结果集中。
- “解集不能包含重复的组合”,就提示我们得对数组先排个序(“升序”或者“降序”均可,下面示例中均使用“升序”)。
- “candidates 中的每个数字在每个组合中只能使用一次”,那就按照顺序依次减去数组中的元素,递归求解即可:遇到 就结算且回溯,遇到负数也回溯。
candidates
中的数字可以重复,可以借助「力扣」第 47 题:“全排列 II” 的思想,在搜索的过程中,找到可能发生重复结果的分支,把它剪去。
(温馨提示:下面的幻灯片中,有几页上有较多的文字,可能需要您停留一下,可以点击右下角的后退 “|?” 或者前进 “?|” 按钮控制幻灯片的播放。)
参考代码 1:以 target 为根结点,依次减去数组中的数字,直到小于 或者等于 ,把等于 的结果记录到结果集中。
感谢用户 @rmokerone 提供的 C++ 版本的参考代码。
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
public class Solution {
<span class="hljs-comment">/**
* <span class="hljs-doctag">@param</span> candidates 候选数组
* <span class="hljs-doctag">@param</span> len
* <span class="hljs-doctag">@param</span> begin 从候选数组的 begin 位置开始搜索
* <span class="hljs-doctag">@param</span> residue 表示剩余,这个值一开始等于 target,基于题目中说明的"所有数字(包括目标数)都是正整数"这个条件
* <span class="hljs-doctag">@param</span> path 从根结点到叶子结点的路径
* <span class="hljs-doctag">@param</span> res
*/</span>
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">dfs</span><span class="hljs-params">(<span class="hljs-keyword">int</span>[] candidates, <span class="hljs-keyword">int</span> len, <span class="hljs-keyword">int</span> begin, <span class="hljs-keyword">int</span> residue, Deque<Integer> path, List<List<Integer>> res)</span> </span>{
<span class="hljs-keyword">if</span> (residue == <span class="hljs-number">0</span>) {
res.add(<span class="hljs-keyword">new</span> ArrayList<>(path));
<span class="hljs-keyword">return</span>;
}
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = begin; i < len; i++) {
<span class="hljs-comment">// 大剪枝</span>
<span class="hljs-keyword">if</span> (residue - candidates[i] < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">break</span>;
}
<span class="hljs-comment">// 小剪枝</span>
<span class="hljs-keyword">if</span> (i > begin && candidates[i] == candidates[i - <span class="hljs-number">1</span>]) {
<span class="hljs-keyword">continue</span>;
}
path.addLast(candidates[i]);
<span class="hljs-comment">// 因为元素不可以重复使用,这里递归传递下去的是 i + 1 而不是 i</span>
dfs(candidates, len, i + <span class="hljs-number">1</span>, residue - candidates[i], path, res);
path.removeLast();
}
}
<span class="hljs-keyword">public</span> List<List<Integer>> combinationSum2(<span class="hljs-keyword">int</span>[] candidates, <span class="hljs-keyword">int</span> target) {
<span class="hljs-keyword">int</span> len = candidates.length;
List<List<Integer>> res = <span class="hljs-keyword">new</span> ArrayList<>();
<span class="hljs-keyword">if</span> (len == <span class="hljs-number">0</span>) {
<span class="hljs-keyword">return</span> res;
}
<span class="hljs-comment">// 先将数组排序,这一步很关键</span>
Arrays.sort(candidates);
Deque<Integer> path = <span class="hljs-keyword">new</span> ArrayDeque<>(len);
dfs(candidates, len, <span class="hljs-number">0</span>, target, path, res);
<span class="hljs-keyword">return</span> res;
}
}
from typing import List
class Solution:
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">combinationSum2</span><span class="hljs-params">(self, candidates: List[int], target: int)</span> -> List[List[int]]:</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">dfs</span><span class="hljs-params">(begin, path, residue)</span>:</span>
<span class="hljs-keyword">if</span> residue == <span class="hljs-number">0</span>:
res.append(path[:])
<span class="hljs-keyword">return</span>
<span class="hljs-keyword">for</span> index <span class="hljs-keyword">in</span> range(begin, size):
<span class="hljs-keyword">if</span> candidates[index] > residue:
<span class="hljs-keyword">break</span>
<span class="hljs-keyword">if</span> index > begin <span class="hljs-keyword">and</span> candidates[index - <span class="hljs-number">1</span>] == candidates[index]:
<span class="hljs-keyword">continue</span>
path.append(candidates[index])
dfs(index + <span class="hljs-number">1</span>, path, residue - candidates[index])
path.pop()
size = len(candidates)
<span class="hljs-keyword">if</span> size == <span class="hljs-number">0</span>:
<span class="hljs-keyword">return</span> []
candidates.sort()
res = []
dfs(<span class="hljs-number">0</span>, [], target)
<span class="hljs-keyword">return</span> res
// author:rmokerone
#include <iostream>
#include <vector>
using namespace std;
class Solution {
private:
vector<int> candidates;
vector<vector<int>> res;
vector<int> path;
public:
void DFS(int start, int target) {
if (target == 0) {
res.push_back(path);
return;
}
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = start; i < candidates.size() && target - candidates[i] >= <span class="hljs-number">0</span>; i++) {
<span class="hljs-keyword">if</span> (i > start && candidates[i] == candidates[i - <span class="hljs-number">1</span>])
<span class="hljs-keyword">continue</span>;
path.push_back(candidates[i]);
<span class="hljs-comment">// 元素不可重复利用,使用下一个即i+1</span>
DFS(i + <span class="hljs-number">1</span>, target - candidates[i]);
path.pop_back();
}
}
<span class="hljs-function"><span class="hljs-built_in">vector</span><<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>>> <span class="hljs-title">combinationSum2</span><span class="hljs-params">(<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>> &candidates, <span class="hljs-keyword">int</span> target)</span> </span>{
sort(candidates.begin(), candidates.end());
<span class="hljs-keyword">this</span>->candidates = candidates;
DFS(<span class="hljs-number">0</span>, target);
<span class="hljs-keyword">return</span> res;
}
};
这里按照用户 @Aspire 提供的思路,给出从 开始,一个使用加法,搜索加到目标数的写法,“前提是排序(升序降序均可)”,然后“剪枝”的操作和上面一样。
参考代码 2:以 为根结点,依次加上数组中的数字,直到大于 target 或者等于 target,把等于 target 的结果记录到结果集中。
#include <iostream>
#include <vector>
#include <map>
using namespace std;
class Solution {
public:
<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>> input;
<span class="hljs-keyword">int</span> target;
<span class="hljs-built_in">vector</span><<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>>> result;
<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>> vc;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">dfs</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index, <span class="hljs-keyword">int</span> sum)</span> </span>{
<span class="hljs-comment">// index >= input.size() ,写成 index == input.size() 即可</span>
<span class="hljs-comment">// 因为每次都 + 1,在 index == input.size() 剪枝就可以了</span>
<span class="hljs-keyword">if</span> (sum >= target || index == input.size()) {
<span class="hljs-keyword">if</span> (sum == target) {
result.push_back(vc);
}
<span class="hljs-keyword">return</span>;
}
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = index; i < input.size(); i++) {
<span class="hljs-keyword">if</span> (input[i] > target) {
<span class="hljs-keyword">continue</span>;
}
<span class="hljs-comment">// 【我添加的代码在这里】:</span>
<span class="hljs-comment">// 1、i > index 表明剪枝的分支一定不是当前层的第 1 个分支</span>
<span class="hljs-comment">// 2、input[i - 1] == input[i] 表明当前选出来的数等于当前层前一个分支选出来的数</span>
<span class="hljs-comment">// 因为前一个分支的候选集合一定大于后一个分支的候选集合</span>
<span class="hljs-comment">// 故后面出现的分支中一定包含了前面分支出现的结果,因此剪枝</span>
<span class="hljs-comment">// “剪枝”的前提是排序,升序或者降序均可</span>
<span class="hljs-keyword">if</span> (i > index && input[i - <span class="hljs-number">1</span>] == input[i]) {
<span class="hljs-keyword">continue</span>;
}
vc.push_back(input[i]);
sum += input[i];
dfs(i + <span class="hljs-number">1</span>, sum);
vc.pop_back();
sum -= input[i];
}
}
<span class="hljs-function"><span class="hljs-built_in">vector</span><<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>>> <span class="hljs-title">combinationSum2</span><span class="hljs-params">(<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>> &candidates, <span class="hljs-keyword">int</span> target)</span> </span>{
<span class="hljs-comment">// “剪枝”的前提是排序,升序或者降序均可</span>
sort(candidates.begin(), candidates.end());
<span class="hljs-keyword">this</span>->input = candidates;
<span class="hljs-keyword">this</span>->target = target;
dfs(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
<span class="hljs-keyword">return</span> result;
}
};
int main() {
cout << "LeetCode 第 40 题:组合问题 II" << endl;
Solution solution = Solution();
<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>> candidates;
candidates.push_back(<span class="hljs-number">10</span>);
candidates.push_back(<span class="hljs-number">1</span>);
candidates.push_back(<span class="hljs-number">2</span>);
candidates.push_back(<span class="hljs-number">7</span>);
candidates.push_back(<span class="hljs-number">6</span>);
candidates.push_back(<span class="hljs-number">1</span>);
candidates.push_back(<span class="hljs-number">5</span>);
<span class="hljs-keyword">int</span> target = <span class="hljs-number">8</span>;
<span class="hljs-built_in">vector</span><<span class="hljs-built_in">vector</span><<span class="hljs-keyword">int</span>>> res = solution.combinationSum2(candidates, target);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < res.size(); ++i) {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j < res[i].size(); ++j) {
<span class="hljs-built_in">cout</span> << res[i][j] << <span class="hljs-string">","</span>;
}
<span class="hljs-built_in">cout</span> << <span class="hljs-string">""</span> << <span class="hljs-built_in">endl</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
https://www.jianshu.com/p/887ca0b25ce5
以上是关于leetcode题解之40. 组合总和 II的主要内容,如果未能解决你的问题,请参考以下文章