回溯算法-组合总和II

目录

组合总和II

1.CodeA

2.CodeB

去重比较

CodeA

CodeB

对比:


组合总和II

力扣题目链接(opens new window)

给定一个数组 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在代码实现上更简洁,同时也更为常见,因此在实际开发中更为常用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值