216. 组合总和 III
难度:☆3
a. 递归法+回溯
递归+回溯的过程可以用树形结构来表示。for
循环:树的宽度,横向遍历。递归:树的深度,纵向遍历。
需要一维数组 path
来存放符合条件的结果,二维数组 result
来存放结果集。取名为 path
是因为结果就是一条从根节点到叶子节点的路径。
剪枝操作有两处:(1)已选元素总和已经大于 n
;(2)k
个元素无法满足,缩小 for
循环的范围。
易错点: for
循环中 i
的取值范围,这里是剪枝操作的位置。因为 i
是从1开始,所以要取到 9-(k-len(path))+1
,即小于 9-(k-len(path))+2
。
Python实现:
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
path = []
result = []
def backtracking(targetSum, k, total, startIndex):
if total > targetSum: # 剪枝
return
if len(path) == k:
if total == targetSum: # 找到符合要求的组合
result.append(path[:]) # 硬拷贝
return
for i in range(startIndex, 9-(k-len(path))+2): # 剪枝
total += i # 处理
path.append(i) # 处理
backtracking(targetSum, k, total, i+1) # 递归
total -= i # 回溯
path.pop() # 回溯
backtracking(n, k, 0, 1)
return result
Java实现:
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
backtracking(k, n, 0, 1);
return result;
}
private void backtracking(int k, int n, int total, int startIndex) {
if (total > n) { // 剪枝
return;
}
if (path.size() == k) { // 终止条件
if (total == n) {
result.add(new ArrayList<>(path));
}
return;
}
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) { // 剪枝
total += i;
path.add(i);
backtracking(k, n, total, i + 1); // 递归
total -= i; // 回溯
path.removeLast(); // 回溯
}
}
}
17. 电话号码的字母组合
难度:☆3
本题是从多个集合求组合。
a. 递归法+回溯
递归+回溯的过程可以用树形结构来表示。for
循环:树的宽度,横向遍历。递归:树的深度,纵向遍历。
数字到字母的映射通过字符串数组,变成下标到字符串的映射。
递归函数参数 index
记录遍历到 digits
的第几个数字,同时 index
也表示树的深度。
- 完全显式回溯。
Python实现:
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
letterMap = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz",
] # 数字到字母的映射:下标到字符串
result = [] # 结果集
def backtracking(digits, index, s):
if index == len(digits): # 终止条件:组合已完整
result.append(s)
return
digit = int(digits[index]) # 取数字
letters = letterMap[digit] # 取数字对应的字母集
for i in range(len(letters)): # 树的宽度,横向遍历
index += 1 # 索引+1处理下一个数字,树的深度,纵向遍历
s += letters[i] # 补充单个字母到结果
backtracking(digits, index, s) # 递归
index -= 1 # 回溯
s = s[:-1] # 回溯
if digits: # 当输入不为空
backtracking(digits, 0, "")
return result
Java实现:
class Solution {
String[] letterMap = { // telephone buttons
"", // 0
"", // 1
"abc", // 2
"def", // 3
"ghi", // 4
"jkl", // 5
"mno", // 6
"pqrs", // 7
"tuv", // 8
"wxyz" // 9
};
StringBuilder s = new StringBuilder();
List<String> result = new ArrayList<>();
public List<String> letterCombinations(String digits) {
if (digits.length() == 0) {
return result;
}
backtracking(digits, 0);
return result;
}
private void backtracking(String digits, int index) {
if (index == digits.length()) { // termination condition
result.add(s.toString());
return;
}
String letter = letterMap[digits.charAt(index) - '0'];
for (int i = 0; i < letter.length(); i++) {
index += 1;
s.append(letter.charAt(i));
backtracking(digits, index); // recursion
index -= 1; // backtracking
s.deleteCharAt(s.length() - 1); // backtracking
}
}
}
- 部分显式回溯。
Python实现:
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
letterMap = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz",
] # 数字到字母的映射:下标到字符串
s = "" # 单个结果
result = [] # 结果集
def backtracking(digits, index, s):
if index == len(digits): # 终止条件:组合已完整
result.append(s)
return
digit = int(digits[index]) # 取数字
letters = letterMap[digit] # 取数字对应的字母集
for i in range(len(letters)): # 树的宽度,横向遍历
s += letters[i] # 补充单个字母到结果
backtracking(digits, index+1, s) # 递归,索引+1处理下一个数字
s = s[:-1] # 回溯
if digits: # 当输入不为空
backtracking(digits, 0, s)
return result
Java实现:
private void backtracking(String digits, int index) {
if (index == digits.length()) { // termination condition
result.add(s.toString());
return;
}
String letter = letterMap[digits.charAt(index) - '0'];
for (int i = 0; i < letter.length(); i++) {
s.append(letter.charAt(i));
backtracking(digits, index + 1); // recursion
s.deleteCharAt(s.length() - 1); // backtracking
}
}
- 隐藏回溯。
Python实现:
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
letterMap = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz",
] # 数字到字母的映射:下标到字符串
result = [] # 结果集
def backtracking(digits, index, s):
if index == len(digits): # 终止条件:组合已完整
result.append(s)
return
digit = int(digits[index]) # 取数字
letters = letterMap[digit] # 取数字对应的字母集
for letter in letters:
backtracking(digits, index+1, s+letter) # 递归+隐藏回溯
if digits: # 当输入不为空
backtracking(digits, 0, "")
return result