目录
组合总和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] ]
1
2
3
4
5
6
- 示例 2:
- 输入: candidates = [2,5,2,1,2], target = 5,
- 所求解集为:
[ [1,2,2], [5] ]
本题将给出两种去重办法
1.CodeA
package com.company; //声明一个名为com.company的包
import java.util.*; //导入Java.util包中的所有类和接口
public class Solution { //定义一个名为Solution的公共类
LinkedList<Integer> path = new LinkedList<>(); //创建一个整数类型的LinkedList类型的路径path
LinkedList<LinkedList> result = new LinkedList<>(); //创建一个LinkedList类型的result,每个元素都是LinkedList类型
public void combineHelper(int[] candidates, int target, int sum, int index) { //定义一个名为combineHelper的公共无返回值方法,需要四个参数
if (sum == target) { //如果sum等于target
result.add(new LinkedList<>(path)); //在结果result中添加一个新的LinkedList类型的路径path
return; //返回
}
if (sum > target) return; //如果sum大于target,返回
int used[]=new int[10000]; //创建一个int类型的used数组,长度为10000
for (int i = index; i < candidates.length; i++) { //循环从index到candidates的长度
if(used[candidates[i]+100]==1 )continue; //如果used[candidates[i]+100]等于1,继续下一个循环
if (sum + candidates[i] > target) break; //如果sum和candidates[i]的和大于target,跳出循环
used[candidates[i]+100] = 1; //将used[candidates[i]+100]设置为1
sum += candidates[i]; //将candidates[i]添加到sum
path.add(candidates[i]); //将candidates[i]添加到路径path
combineHelper(candidates,target, sum, i+1); //递归调用combineHelper方法,传递参数candidates, target, sum和i+1
sum -= candidates[i]; //从sum中减去candidates[i]
path.removeLast(); //从路径path中移除最后一个元素
}
}
public static void main(String[] args) { //定义一个名为main的公共静态方法,返回值为void
int d[] = {10,1,2,7,6,1,5}; //定义一个名为d的整数类型数组,包含多个整数值
int vist[]=new int[10000]; //定义一个名为vist的整数类型数组,长度为10000
int a = (int) System.currentTimeMillis(); //获取当前时间并将其赋值给整数类型的变量a
Solution obj = new Solution(); //创建一个名为obj的Solution类型的实例
Arrays.sort(d); //将数组d进行排序
obj.combineHelper(d,8, 0, 0); //调用obj的combineHelper方法,传递参数d, 8, 0和0
System.out.println(obj.result); //输出结果result
int b = (int) System.currentTimeMillis(); //获取当前时间并将其赋值给整数类型的变量b
System.out.println("时间:" + (b - a)); //输出计算时间
}
}
2.CodeB
package com.company;
import java.util.*;
public class Main {
LinkedList<Integer> path = new LinkedList<>(); // 记录当前组合的路径
LinkedList<LinkedList> result = new LinkedList<>(); // 记录所有符合要求的组合
public void combineHelper(int[] candidates, int[] vist, int target, int sum, int index) {
// 判断当前组合是否满足条件,满足则添加到结果中
if (sum == target) {
result.add(new LinkedList<>(path));
return;
}
// 如果当前和大于目标值,直接返回
if (sum > target) return;
// 对于每一个候选元素,进行组合
for (int i = index; i < candidates.length; i++) {
// 如果当前元素与上一个元素相同,并且上一个元素未被访问过,则跳过当前元素
if (i > 0 && candidates[i - 1] == candidates[i] && vist[i - 1] == 0) continue;
// 如果当前和加上当前元素已经大于目标值,则退出循环
if (sum + candidates[i] > target) break;
// 标记当前元素已经访问过
vist[i] = 1;
// 更新当前和和路径
sum += candidates[i];
path.add(candidates[i]);
// 继续搜索下一个元素
combineHelper(candidates, vist, target, sum, i+1);
// 回溯,撤销更新
sum -= candidates[i];
path.removeLast();
vist[i] = 0;
}
}
public static void main(String[] args) {
int d[] = {10,1,2,7,6,1,5};
int vist[] = new int[10000];
int a = (int) System.currentTimeMillis();
Main obj = new Main();
Arrays.sort(d); // 对候选元素进行排序,方便去重
obj.combineHelper(d, vist, 8, 0, 0);
System.out.println(obj.result);
int b = (int) System.currentTimeMillis();
System.out.println("时间:" + (b - a));
}
}
去重比较
CodeA
public void combineHelper(int[] candidates, int target, int sum, int index) {
if (sum == target) {
result.add(new LinkedList<>(path));
return;
}
if (sum > target) return;
int used[]=new int[10000];
for (int i = index; i < candidates.length; i++) {
if(used[candidates[i]+100]==1 )continue;
if (sum + candidates[i] > target) break;
used[candidates[i]+100] = 1;
sum += candidates[i];
path.add(candidates[i]);
combineHelper(candidates,target, sum, i+1);
sum -= candidates[i];
path.removeLast();
}
}
CodeB
public void combineHelper(int[] candidates, int[] vist, int target, int sum, int index) {
if (sum == target) {
result.add(new LinkedList<>(path));
return;
}
if (sum > target) return;
for (int i = index; i < candidates.length; i++) {
if( i>0&&candidates[i-1]==candidates[i]&&vist[i-1]==0 )continue;
if (sum + candidates[i] > target) break;
vist[i] = 1;
sum += candidates[i];
path.add(candidates[i]);
combineHelper(candidates, vist, target, sum, i+1);
sum -= candidates[i];
path.removeLast();
vist[i] = 0;
}
}
对比:
Code A中使用了一个数组used来记录已经使用的元素,用来去重。具体而言,在for循环中,每次都检查当前元素是否已经被标记使用过,如果是,直接跳过。
Code B中使用了一个数组vist来记录元素是否被访问过,用来去重。具体而言,在for循环中,每次都检查当前元素是否与前一个元素相同,如果相同,并且前一个元素没有被访问过,那么当前元素也直接跳过。
这两种去重方法的本质区别在于对重复元素的处理方式。Code A是在元素使用时标记,Code B是在元素访问时标记。Code B在代码实现上更简洁,同时也更为常见,因此在实际开发中更为常用。