40. Combination Sum II

本文详细解析了LeetCode上的第40题——Combination Sum II,对比第39题,阐述了两者的区别及解决方案。重点介绍了如何避免重复组合,通过递归回溯算法实现所有可能的组合。

题目

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

Each number in candidates may only be used once in the combination.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.

我的想法

这道题与39. Combination Sum不同之处在于:
39题candidates不允许重复元素,但是结果中可以重复使用元素。
40题candidates允许重复元素,但结果中每个元素只能使用一次(1,1中两个1不同)

因此,为了能重复使用相同,元素39题迭代时,start = i直接从当前循环数开始;而为了避免重复使用元素,40题迭代时start = i+1需要从当前循环数的下一个数开始

因为这里的candidates允许重复元素,所以可能出现同一种组合出现多次的情况,比如:[1,1,1,2,3,3,5] target = 4
重复情况有两种:

  1. 首数字重复,即上述例子中,如果target = 8[3,5]的组合有2种。这种情况下,重复的组合不出现在同一个循环中。如果是首数字,其start一定为0,因此只要是满足(start == 0 && i > 0) && candidates[i] == candidates[i-1]则跳过
  2. 非首数字重复,即上述例子中,如果target = 4[1,3]的组合有6种。这种情况下,重复的组合出现在同一个循环中,因此满足(i > start) && candidates[i] == candidates[i-1]则跳过
class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> ls = new ArrayList<>();
        Arrays.sort(candidates);
        backtrack(candidates, ls, new ArrayList<Integer>(), 0, 0, target);
        return ls;
    }
    private void backtrack(int[] candidates, List<List<Integer>> ls, List<Integer> temp, int start, int count, int target) {
        if(count == target) {
            ls.add(new ArrayList<Integer>(temp));
            return;
        }
        for(int i = start; i < candidates.length; i++) {
            if(count + candidates[i] > target) continue;
            //滤去重复项
            if(((i > start) || (start == 0 && i > 0)) &&
              candidates[i] == candidates[i-1]) continue;

            temp.add(candidates[i]);
            count += candidates[i];
            backtrack(candidates, ls, temp, i+1, count, target);
            count -= temp.remove(temp.size() - 1);
        }
    }
}

解答

leetcode solution: Backtrack
别人的写法更加简练。但是性能更差,原因待分析

public List<List<Integer>> combinationSum2(int[] candidates, int target) {
   List<List<Integer>> list = new LinkedList<List<Integer>>();
   Arrays.sort(candidates);
   backtrack(list, new ArrayList<Integer>(), candidates, target, 0);
   return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] cand, int remain, int start) {
   
   if(remain < 0) return; /** no solution */
   else if(remain == 0) list.add(new ArrayList<>(tempList));
   else{
      for (int i = start; i < cand.length; i++) {
         if(i > start && cand[i] == cand[i-1]) continue; /** skip duplicates */
         tempList.add(cand[i]);
         //改变target的值来进行控制
         backtrack(list, tempList, cand, remain - cand[i], i+1);
         tempList.remove(tempList.size() - 1);
      }
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值