用回溯法来解决组合问题,本质上也是一种暴力搜索的方式。它其实可以看做是一种递归搜索,在组合问题中,我们就是在每一层选中一个或多个元素,然后不断递归,直至找到满足条件的组合。然后在过程中不断进行回溯,我个人感觉也可以用 释放 来表明其含义 。
在carl哥的代码随想录中给出了一张图非常的通俗易懂 ,里面讲解的也更加全面,大家可以去参考里面的讲解内容,个人感觉还是非常有帮助的。
-
力扣(LeetCode)22 括号生成
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
class Solution {
StringBuilder path = new StringBuilder();
List<String> res = new ArrayList<>();
public List<String> generateParenthesis(int n) {
back(n,0,0);
return res;
}
public void back(int n,int left,int right){
if(left+right == n*2){
res.add(path.toString());
return;
}
if(left < n){
path.append("(");
back(n,left+1,right);
//进行回溯
path.deleteCharAt(path.length()-1);
}
if(right < left){
path.append(")");
back(n,left,right+1);
//进行回溯
path.deleteCharAt(path.length()-1);
}
}
}
-
力扣(LeetCode)77 组合
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。你可以按 任何顺序 返回答案。
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
fun(n,k,1);
return result;
}
public void fun(int n,int k,int start){
if(path.size() == k){
result.add(new ArrayList<>(path));
return;
}
for(int i= start; i <= n - (k - path.size()) + 1;i++){
path.add(i);
fun(n,k,i+1);
path.removeLast();
}
}
}
-
力扣(LeetCode)39 组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。s
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
fun(candidates,target,0,0);
return result;
}
public void fun(int[] candidates,int target,int sum,int idx){
if(sum == target){
result.add(new ArrayList<>(path));
return;
}
for(int i=idx;i<candidates.length;i++){
if(sum + candidates[i]>target) break;
path.add(candidates[i]);
fun(candidates,target,sum+candidates[i],i);
path.remove(path.size()-1);
}
}
}
-
力扣(LeetCode)40 组合总和2
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
boolean[] used;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
used = new boolean[candidates.length];
Arrays.sort(candidates);
fun(candidates,target,0,0);
return res;
}
public void fun(int[] candidates,int target,int sum,int start){
if(sum == target){
res.add(new ArrayList<>(path));
}
for(int i=start;i<candidates.length ;i++){
if(sum + candidates[i] > target)break;
if(i>0&&candidates[i] == candidates[i-1] && used[i-1] == false) continue;
path.add(candidates[i]);
sum+=candidates[i];
used[i] = true;
fun(candidates,target,sum,i+1);
sum-=candidates[i];
path.removeLast();
used[i] = false;
}
}
}
-
力扣(LeetCode)216 组合总和3
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9,每个数字 最多使用一次 ,返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
fun(n,k,1,0);
return result;
}
public void fun(int target,int k,int start,int sum){
if(sum >target ) return;
if(path.size() == k){
if(sum == target){
result.add(new ArrayList<>(path));
return;
}
}
for(int i=start;i<=9-(k-path.size())+1;i++){
path.add(i);
sum+=i;
fun(target,k,i+1,sum);
path.removeLast();
sum-=i;
}
}
}
-
力扣(LeetCode)377 组合总和4
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
这题看起来和上面的题目长得差不多,但是因为这题没有重复的限制,并且一个元素可以重复使用,那么对于nums中各个元素较小且所求的target较大时,所需要的递归次数就非常吓人了。
我在自己电脑上运行了下面这段用基本回溯思想写的代码,实例是nums = [2,1,3] target = 35,所运行的时间达到了14秒多,肯定是超时了。上面的数字是组合的种类
class Solution {
int sum = 0;
int res = 0;
public int combinationSum4(int[] nums, int target) {
fun(nums,target);
return res;
}
public void fun(int[] nums,int target){
if(sum > target){
return;
}
if(sum == target){
res++;
return;
}
for(int i = 0;i<nums.length;i++){
sum+=nums[i];
fun(nums,target);
sum-=nums[i];
}
}
}
这题可以用动归的思想来做,虽然很难想到。
class Solution {
public int combinationSum4(int[] nums, int target) {
int len = nums.length;
int[] dp = new int[target+1];
dp[0] = 1;
for(int i = 0;i<=target;i++)
for(int j = 0;j<len;j++){
if(i>=nums[j]){
dp[i]+= dp[i-nums[j]];
}
}
return dp[target];
}
}
仅作为个人笔记使用,如有错误还需海涵。