77.组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
输入:n = 4, k = 2
输出:
[[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],]
import java.util.ArrayList;
import java.util.List;
class Solution {
public List<List<Integer>> combine(int n, int k) {
backTrack(1, n, k);
return res;
}
private List<Integer> path = new ArrayList<>();
private List<List<Integer>> res = new ArrayList<>();
private void backTrack(int start, int n, int k) {
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i <= n - (k - path.size()) + 1; i++) {
path.add(i);
backTrack(i + 1, n, k);
path.remove(path.size() - 1);
}
}
}
216.组合总和III
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
import java.util.ArrayList;
import java.util.List;
class Solution {
public List<List<Integer>> combinationSum3(int k, int n) {
backTrace(1, 0, n, k);
return res;
}
private List<Integer> path = new ArrayList<>();
private List<List<Integer>> res = new ArrayList<>();
private void backTrace(int start, int sum, int n, int k) {
if (path.size() == k && sum == n) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i <= 9 - (k - path.size()) + 1 && sum < n; i++) {
path.add(i);
sum += i;
backTrace(i + 1, sum, n, k);
sum -= i;
path.remove(path.size() - 1);
}
}
}
17.电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
输入:digits = “23”
输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]
public List<String> letterCombinations(String digits) {
if (digits.equals("")) {
return new ArrayList<>();
}
Map<String, String> map = new TreeMap<>();
map.put("2", "abc");
map.put("3", "def");
map.put("4", "ghi");
map.put("5", "jkl");
map.put("6", "mno");
map.put("7", "pqrs");
map.put("8", "tuv");
map.put("9", "wxyz");
backTrace(0, digits, map);
return res;
}
private List<String> res = new ArrayList<>();
private StringBuilder sb = new StringBuilder();
private void backTrace(int start, String s, Map<String, String> map) {
if (sb.length() == s.length()) {
res.add(new String(sb));
return;
}
String tmpString = map.get(s.substring(start, start + 1));
for (int i = 0; i < tmpString.length(); i++) {
sb.append(tmpString.substring(i, i + 1));
backTrace(start + 1, s, map);
sb.delete(sb.length() - 1, sb.length());
}
}
39.组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backTrace(candidates, 0, target,0);
return res;
}
private List<List<Integer>> res = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
private void backTrace(int[] nums, int sum, int target, int start) {
if (sum > target) {
return;
}
if (sum == target) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i < nums.length; i++) {
sum += nums[i];
path.add(nums[i]);
backTrace(nums, sum, target, i);
sum -= nums[i];
path.remove(path.size() - 1);
}
}
这里要注意,题目中说数字无限选取,不是每次都能遍历12345…n(这样会出现[2,2,3],[2,3,2],[3,2,2]这样重复的数组),还是要有startIdx的,只不过比单次使用的回溯情况下,多了个纵向回溯时从自身开始(单次使用元素是从自身下一个开始)。
40.组合总和II
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[[1,1,6],
[1,2,5],
[1,7],
[2,6]]
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backTrace(candidates, 0, target,0);
return res;
}
private List<List<Integer>> res = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
private void backTrace(int[] nums, int sum, int target, int start) {
if (sum > target) {
return;
}
if (sum == target) {
res.add(new ArrayList<>(path));
return;
}
for (int i = start; i < nums.length; i++) {
if (i > start && nums[i] == nums[i - 1]) {
continue;
}
sum += nums[i];
path.add(nums[i]);
backTrace(nums, sum, target, i + 1);
sum -= nums[i];
path.remove(path.size() - 1);
}
}
}
因为数组里有重复的数字,比如1,1,2,3,就会在第一个1和第二个1处各出现个[1,2,3],这样就重复了,所以要在回溯的过程中去重,怎么去重呢,因为for循环是管横向遍历的,递归是管纵向遍历的,所以在横向遍历数组的时候,去掉和前一个相同的元素,这里要非常注意条件if (i > start && nums[i] == nums[i - 1]) ,一定要i>start,因为start位置一定是要递归的,但是start后面与start位置相同的元素就要跳过了,千万别再写成i>1了…[1,1,2,5,6,7,10]这样的数组,i>1就没去掉第二个1,i>=1也不行,这样会把start去掉的