39 Combination Sum

题目链接:https://leetcode.com/problems/combination-sum/

题目:

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

The same repeated number may be chosen from C unlimited number of times.

Note:
All numbers (including target) will be positive integers.
Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
The solution set must not contain duplicate combinations.
For example, given candidate set 2,3,6,7 and target 7, 
A solution set is: 
[7] 
[2, 2, 3] 

解题思路:
以 [7, 3, 2] 18 为例说明思路

  1. 先把已知数组按从小到大的顺序排好。[2, 3, 7]
  2. 从最大的元素 7 开始,依次向较小元素的遍历
  3. 首先,判断 target(18) 能容纳多少个当前元素(7),18 / 7 = 2,18 最多能容纳 2 个 7,表明包含当前元素 7 的列表有两个,分别为包含一个 7 的列表[7, …] 和两个 7 的列表[7, 7, …]
  4. 然后,分别对两个列表进行递归。此时,对于第一个列表 target 变为 18 - 7 = 11,第二个列表 target 为 18 - 14 = 4。递归时,起始元素为比当前元素(7)小的元素,由于我们已经对原数组进行了排序,所以把当前元素 7 前面的元素 3 和 2 分别作为起始元素进行递归。也就是包含了两种情况,第一种,起始元素为3 [7, 3, …],第二种,[7, 7, 2, …]起始元素为 2,没有经过 3
  5. 以此类推,直到 target 为 0(找到所有元素),或者 target 小于数组中最小的元素(找不到符合要求的元素),递归结束。

简单来说

  1. 对原数组进行排序。
  2. 从最大的元素开始,根据目标数所能包含的元素个数,用目标数减去相应的元素的值。
    例如,目标数能包含3个元素,则用目标数分别减去1个元素,两个元素,和三个元素。
  3. 剩下的值为下一轮递归的目标数,且下一轮递归从当前轮的元素的前面元素开始(分别以比当前元素小的元素作为起始元素)。
public class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> list = new ArrayList();
        if(candidates == null || candidates.length == 0)
            return list;
        Arrays.sort(candidates);
        for(int i = candidates.length - 1; i >= 0; i --) {
            if(candidates[i] > target)
                continue;
            List<List<Integer>> subList = help(candidates, target, i);
            for(List<Integer> l : subList) {
                if(l != null && !list.contains(l))
                    list.add(l);
            }
        }
        return list;
    }
    public List<List<Integer>> help(int[] candidates, int target, int k) {// k为元素下标
        int n = target / candidates[k];
        if(n < 1)
            return null;
        else { // n >= 1
            List<List<Integer>> res = new ArrayList();
            for(int i = 1; i <= n; i ++) {
                List<Integer> list = new ArrayList();
                for(int j = 0; j < i; j ++)
                    list.add(candidates[k]);
                int find = target - i * candidates[k];
                if(find == 0)
                    res.add(list);
                else { // find > 0
                    if(k > 0) {
                        for(int t = 1; t <= k; t ++) {
                            List<List<Integer>> subList = help(candidates, find, k - t);
                            if(subList != null) {
                                for(List<Integer> l : subList) {
                                        List<Integer> temp = new ArrayList(list);
                                        temp.addAll(l);
                                        Collections.sort(temp);
                                        res.add(temp);
                                }
                            }
                        }
                    }
                }
            }
            return res;
        }
    }
}
168 / 168 test cases passed.
Status: Accepted
Runtime: 316 ms

值得借鉴的另一种方法:http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/linhuanmars/article/details/20828631

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值