java数据结构与算法刷题-----LeetCode40. 组合总和 II

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.youkuaiyun.com/grd_java/article/details/123063846

在这里插入图片描述

解题思路
  1. 39题衍生题,39题基础上,加了一个条件,candidates数组中每个数字在同一个组合中只能使用一次。这些数字是有可能相同的。
  2. 这道题的难点在于,[1,7,1,6]为例,[1,1,6],[1,7],[7,1]都满足target = 8的条件,但是返回结果中不能同时返回[1,7]和[7,1]因为这两个组合是同一个,是重复的。
  3. 所以我们先将数组排序为[1,1,6,7],当我们某个位置枚举时,如果和上次一样就跳过
  1. 例如,我们第一个位置先选用[1,1,6,7]标黄的1,枚举出[1,1,6],[1,7],此时这个1就毕业了,它会腾出第一个位置
  2. 不跳过的情况下:我们第一个位置会直接用第二个位置的1,从而枚举出[1,7]. 此时就会枚举出两个相同的组合[1,7]和[1,7]
  3. 但是,现在我们,要切换下一个值来当前位置时。先比较下一个值是否和当前值一样,如果一样就跳过。从而不再次枚举[1,7]。
  4. 我们发现1 = 1,所以我们跳过这个1.然后继续判断比较后面的值6,发现不一样,那么下次就用6在第一个位置枚举
🏆LeetCode39. 组合总和https://blog.youkuaiyun.com/grd_java/article/details/136583880
整个代码,与39题代码的唯一区别,就是增加了先排序,然后相同位置,如果下次枚举还是一样的值,就跳过的逻辑

与39题代码唯二不一样的地方如下:

1. 实现方式1:典型回溯

  1. 仅仅修改了39题的代码,增加了两行代码
    在这里插入图片描述
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;

public class Solution {

    int[] candidates;
    int target;
    int length;
    List<List<Integer>> lists;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);//增加排序逻辑
        this.candidates = candidates; this.target = target; this.length = candidates.length;
        this.lists = new ArrayList<List<Integer>>();
        Integer[] records = new Integer[length];
        backTracking(records,0,0,0);
        return lists;
    }
    public void backTracking(Integer[] records,int row,int sum,int index){
        if(index >=length) return;
        else if(sum > target)return;
        else{
            records[row] = candidates[index];
            int curSum = sum+records[row];
            if(curSum>target) return;
            if(curSum == target) {
                List<Integer> list = new ArrayList<>();
                for(int i = 0;i<=row;i++) list.add(records[i]);
                lists.add(list);
            }else{
                backTracking(records,row+1,curSum,index+1);
            }
            while(index<length-1 && candidates[index]==candidates[index+1]) index++;//跳过相同位置枚举相同值的情况
            backTracking(records,row,sum,index+1);
        }
    }
}

2. 实现方式2:模拟树的深度优先遍历

  1. 此方式,将回溯算法,当成树来进行深度遍历。
    在这里插入图片描述
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;

public class Solution {

    int[] candidates;
    int len;
    List<List<Integer>> res;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        this.len = candidates.length;
        res = new ArrayList<>();
        if (len == 0) return res;
        // 关键步骤
        Arrays.sort(candidates);
        this.candidates = candidates;
        Deque<Integer> path = new ArrayDeque<>(len);
        dfs(target,0,path);
        return res;
    }

    /**
     * @param begin      从候选数组的 begin 位置开始搜索
     * @param path       从根结点到叶子结点的路径
     */
    private void dfs(int target,int begin, Deque<Integer> path) {
        if (target == 0) {//如果当前路径结点的和,正好是target
            res.add(new ArrayList<>(path));//添加这条路径
            return;
        }
        for (int i = begin; i < len; i++) {//否则当前结点,可以是剩余不重复值的任意一个(当前层可以取哪些值)
            // 大剪枝:减去 candidates[i] 小于 0,减去后面的 candidates[i + 1]、candidates[i + 2] 肯定也小于 0,因此用 break
            if (target - candidates[i] < 0) break;
            // 小剪枝:同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发生重复,因此跳过,用 continue
            if (i > begin && candidates[i] == candidates[i - 1]) continue;
            path.addLast(candidates[i]);
            // 因为元素不可以重复使用,这里递归传递下去的是 i + 1 而不是 i
            dfs(target - candidates[i],i + 1,  path);
            path.removeLast();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ydenergy_殷志鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值