这几题挺相似的,解法大同小异
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;
}
}
}