五道回溯找组合题40_77_78_90_216

这几题挺相似的,解法大同小异

40. Combination Sum II

从一个数组中找到所有和等于target的子数组,数组中可能含有重复元素,每个元素只能使用一次。

Input: candidates = [2,5,2,1,2], target = 5, A solution set is:
[ [1,2,2], [5] ]

39题进阶版,就是可能包含重复元素而已,解法几乎一样,只要在递归回溯时判断一个下一个元素和当前元素是否相同即可,若相同跳过:

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

List<List<Integer>> res = new ArrayList<>();
public void help(int[] candidates, int target, int sum, List<Integer> nums, int index) {
    if (sum == target) {
        res.add(new ArrayList<>(nums));
    } else {
        for (; index < candidates.length; index++) {
            if (sum + candidates[index] <= target) {
                nums.add(candidates[index]);
                // System.out.println(nums);
                help(candidates, target, sum+candidates[index], nums, index+1);
                nums.remove(nums.size()-1);
                while (index + 1 < candidates.length && candidates[index+1] == candidates[index]) index++;
            } else
                break;
        }
    }
}

77. Combinations

给一个n和一个k,返回所有由k个数字组成的数组,数字的范围是[1,n]。

Input: n = 4, k = 2
Output:
[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4] ]

和上面的题几乎没有区别嘛,只不过这个数组由1到n组成,且不重复。还有就是可以进行剪枝加快速度:

public List<List<Integer>> combine(int n, int k) {
    help(n, k, 0, 1, new ArrayList<>());
    return res;
}

List<List<Integer>> res = new ArrayList<>();
public void help(int n, int k, int len, int index, List<Integer> nums) {
    if (len == k) {
        res.add(new ArrayList<>(nums));
        return;
    } else {
        //当剩余元素个数少于还需要的元素个数时,进行剪枝
        if (n - index + 1 >= k - len) {
            for (; index < n + 1; index++) {
                nums.add(index);
                help(n, k, len + 1, index + 1, nums);
                nums.remove(nums.size() - 1);
            }
        }
    }
}

78. Subsets

给一个没有重复元素的数组,返回所有的子数组。

Input: nums = [1,2]
Output:
[ [1], [2], [1,2], [] ]

解法几乎还是一样嘛,只不过不用判断这个子数组是否符合要求,只要是个新的子数组就直接加入解集合中:

public List<List<Integer>> subsets(int[] nums) {
    help(nums, new ArrayList<>(), 0);
    return res;
}

List<List<Integer>> res = new ArrayList<>();
public void help(int[] nums, List<Integer> oneRes, int index) {
    res.add(new ArrayList<>(oneRes));

    for (int i = index; i < nums.length; i++) {
        oneRes.add(nums[i]);
        help(nums, oneRes, i+1);
        oneRes.remove(oneRes.size()-1);
    }
}

90. Subsets II

给一个含重复元素的数组,返回所有的子数组。

Input: [1,2,2]
Output:
[ [2], [1], [1,2,2], [2,2], [1,2], [] ]

和上面那题的区别就是数组中可能含有重复元素,所以在每次递归回溯时判断下一个元素是否和当前的相同,相同的话跳过:

public List<List<Integer>> subsetsWithDup(int[] nums) {
  Arrays.sort(nums);
      help(nums, new ArrayList<>(), 0);
    return res;
}

List<List<Integer>> res = new ArrayList<>();
public void help(int[] nums, List<Integer> one, int index) {
    res.add(new ArrayList<>(one));

    for (int i = index; i < nums.length; i++) {
        one.add(nums[i]);
        help(nums, one, i+1);
        one.remove(one.size()-1);
        //唯一区别
        while (i+1 < nums.length && nums[i] == nums[i+1]) i++;
    }
}

216. Combination Sum III

给一个n和一个k,找到所有和等于n,长度等于k的数组,每个元素在1到9之间,元素不可重复。

40题的升级版,其实还是差不多,相当于给了一个数组[1,2,3,4,5,6,7,8,9],并且要求了子数组的长度必须为k,而且还没有重复元素,所以更简单了。另外也可以进行剪枝加快速度:

public List<List<Integer>> combinationSum3(int k, int n) {
    help(k, n, 0, 0, 1, new ArrayList<>());
    return res;
}

List<List<Integer>> res = new ArrayList<>();
public void help(int k, int n, int len, int sum, int index, List<Integer> one) {
    if (len == k && sum == n) {
        res.add(new ArrayList<>(one));
    }else {
        for (int i = index; i < 10; i++) {
        	//剪枝
            if (sum + i <= n && len < k) {
                one.add(i);
                help(k, n, len + 1, sum + i, i + 1, one);
                one.remove(one.size() - 1);
            } else
                break;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值