第二十五天| 第七章 回溯算法part02 216. 组合总和III 17. 电话号码的字母组合
一、216. 组合总和III
-
题目链接:https://leetcode.cn/problems/combination-sum-iii/submissions/
-
题目介绍:
-
找出所有相加之和为
n
的k
个数的组合,且满足下列条件:- 只使用数字1到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
-
-
思路:
- 思路和组合类似,只是需要一个sum来控制放入path中的数值
-
代码:
-
未剪枝
-
class Solution { LinkedList<Integer> path = new LinkedList<>(); List<List<Integer>> result = new ArrayList<>(); public List<List<Integer>> combinationSum3(int k, int n) { backtracking(k, n, 0, 1); return result; } // 1. 确定返回值和参数 public void backtracking(int k, int n, int sum, int startIndex) { // 2. 确定终止条件 if (sum == n && path.size() == k) { result.add(new ArrayList<>(path)); return; } // 3. 单层搜索逻辑 for (int i = startIndex; i <= 9; i++) { // 3.1 操作单个节点 sum += i; path.add(i); // 3.2 递归 backtracking(k, n, sum, i + 1); // 3.3 回溯 sum -= i; path.removeLast(); } return; } }
-
-
剪枝
-
有两处可以剪枝:
- (1)如果sum都大于n了,直接return,返回上一层。(这里可以直接看作是一个终止条件)
- (2)和组合一样的的剪枝思路,集合中剩余元素的个数必须大于等于需要向path中add的个数,即9 - i + 1 >= k - path.size()。所以:i <= 10 - (k - path.size())
-
class Solution { LinkedList<Integer> path = new LinkedList<>(); List<List<Integer>> result = new ArrayList<>(); public List<List<Integer>> combinationSum3(int k, int n) { backtracking(k, n, 0, 1); return result; } // 1. 确定返回值和参数 public void backtracking(int k, int n, int sum, int startIndex) { // 补充:剪枝 // 把这个剪枝当作一个终止条件来看 if (sum > n) return; // 2. 确定终止条件 if (sum == n && path.size() == k) { result.add(new ArrayList<>(path)); return; } // 3. 单层搜索逻辑 // 补充:剪枝 for (int i = startIndex; i <= 10 - (k - path.size()); i++) { // 3.1 操作单个节点 sum += i; path.add(i); // 3.2 递归 backtracking(k, n, sum, i + 1); // 3.3 回溯 sum -= i; path.removeLast(); } return; } }
-
-
二、17. 电话号码的字母组合
- 题目链接:https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
- 题目介绍:
- 给定一个仅包含数字
2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 - 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
- 给定一个仅包含数字
- 思路:
- 代码:
class Solution {
List<String> result = new ArrayList<>();
StringBuilder sb = new StringBuilder();
public List<String> letterCombinations(String digits) {
if (digits == null || digits.length() == 0) return result;
String[] mappingArray = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
backtracking(digits, mappingArray, 0);
return result;
}
// 1. 确定返回值和参数
// 1.1 返回值:void
// 1.2 参数:需要传入原始的数字字符串、一个映射的字符串数组、数字字符串的下标
public void backtracking(String digits, String[] mappingArray, int index) {
// 2. 终止条件:如果index等于数字字符串的size说明遍历到了叶子节点,就return
if (index == digits.length()) {
result.add(sb.toString());
return;
}
// 3. 单层的搜索逻辑
String mappingStr = mappingArray[digits.charAt(index) - '0'];
for (int i = 0; i < mappingStr.length(); i++) {
// 3.1 操作集合中的元素
sb.append(mappingStr.charAt(i));
// 3.2 递归
backtracking(digits, mappingArray, index + 1);
// 3.3 回溯
sb.deleteCharAt(sb.length() - 1);
}
return;
}
}
- 注意点:
- (1)与“组合”题目不同的是,电话号码的字母组合遍历的是不同的集合。因此,不能使用在“组合”问题中的startIndex,而是定义一个index遍历数字字符串;
- (2)每一层中放置的是一个数字字符串中元素对应的字母字符串,因此第一层的index=0,即如果字符串的长度为n,那么最后一层的index为n - 1;
- (3)这里回溯的时候,不能像“组合”问题一样,add进入的是什么removeLast的就是什么;因为在本题中,循环对应的是每一层的一个集合,StringBuilder中append的是不同集合中的不同元素,所以在回溯的时候移除StringBuilder的最后一个元素,使用StringBuilder.deleteCharAt(sb.length() - 1)