代码随想录算法训练营第29天 || 216.组合总和III || 17.电话号码的字母组合
216.组合总和III
题目简介:
找出所有相加之和为 n
的 k
个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。
示例 3:
输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。
个人思路:
简单暴力法,无法去嵌套n(动态)层for循环,只能考虑使用递归
回溯三部曲
- 确定参数和返回值
- 参数:基本的k、n和startIndex、sum
- 返回值:void
- 确定递归结束条件:
list.size == k
时,若还满足sum == n
,则保存结果 - 确定单层递归逻辑及剪枝:
for (int i = startIndex; i <= 10 - k + list.size() && list.size() < k; i++) {//根据个数限制剪枝
if (sum + i > n) //剪枝操作,发现不符直接跳出循环,不必执行下面的操作
break;
list.add(i);
backtracking(k, n, i + 1, sum + i);
list.remove(list.size() - 1);//回溯操作
}
完整代码:
class Solution {
public List<List<Integer>> result = new ArrayList<>();
public List<Integer> list = new ArrayList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
backtracking(k, n, 1, 0);
return result;
}
public void backtracking(int k, int n, int startIndex, int sum) {
if (list.size() == k && sum == n) {
result.add(new ArrayList<>(list));
return;
}
//两个剪枝
//1.往后递归数量也不够k个(列个方程即可
//2.当前递归sum已经大于n,没必要再往下了
for (int i = startIndex; i <= 10 - k + list.size() && list.size() < k; i++) {
if (sum + i > n)
break;
list.add(i);
backtracking(k, n, i + 1, sum + i);
list.remove(list.size() - 1);
}
}
}
17.电话号码的字母组合
题目简介:
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
个人思路:
同样是模拟n层循环,只能考虑使用回溯法
这道题8个数字,有的数字对应3个字母有的对应4个字母,直接函数对应ASCII码比较困难,所以考虑使用map表存储
回溯三部曲:
- 确定参数和返回值:
- 参数:char数组,StringBuild 临时字符串,得到每个结果进行保存
- 返回值:void
- 确定递归结束条件:数组长度为0就结束,同时StringBuild的长度不为0才保存结果
- 确定单层递归逻辑:
- 先确定for循环的范围(4或3)
- for循环中根据hash取对应字母,进行回溯操作即可
class Solution {
public List<String> result = new ArrayList<>();
public HashMap<Integer, Character> hashMap = new HashMap<>();
public List<String> letterCombinations(String digits) {
initHash();
backtracking(digits.toCharArray(), new StringBuilder());
return result;
}
public void backtracking(char[] str, StringBuilder stringTemp) {
if (str.length == 0) {
if (stringTemp.length() != 0)//避免添加空白字符串
result.add(new String(stringTemp.toString()));
return;
}
int num = str[0] == '7' || str[0] == '9' ? 4 : 3;
for (int j = 1; j <= num; j++) { //每个数字最多对应4个字符
Character temp = hashMap.get((str[0] - '0') * 10 + j);
stringTemp.append(temp);
backtracking(Arrays.copyOfRange(str, 1, str.length), stringTemp);
stringTemp.deleteCharAt(stringTemp.length() - 1);
}
}
//初始化Hash表
public void initHash() {
char letter = 'a';
for (int i = 2; i <= 6; i++) {
for (int j = 1; j <= 3; j++) {
hashMap.put(i * 10 + j, letter++);
}
}
for (int i = 1; i <= 4; i++)
hashMap.put(70 + i, letter++);
for (int i = 1; i <= 3; i++)
hashMap.put(80 + i, letter++);
for (int i = 1; i <= 4; i++)
hashMap.put(90 + i, letter++);
}
}
题解解析:
基本思路都是使用Hash加回溯,但相比自己的代码还是优化了不少
- Hash表上的优化,使用一个String一维数组替代HashMap,提高效率,节省空间
- 回溯参数上的优化,传入数组和下标值,避免了本人代码中每次复制数组的操作
- 隐式回溯的优化,在传参字符串中,可以传入s+digits[i];
class Solution {
public List<String> result = new ArrayList<>();
public String[] array = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
public List<String> letterCombinations(String digits) {
if (digits == null || digits.length() == 0)
return result;
backtracking(digits.toCharArray(), 0, new StringBuilder());
return result;
}
public void backtracking(char[] str, int index, StringBuilder stringTemp) {
if (str.length == index) {
result.add(new String(stringTemp.toString()));//也可以将stringTemp设为全局变量,这里就不必new一个一样的
return;
}
String s = array[str[index] - '2'];
for (int j = 0; j < s.length(); j++) { //每个数字最多对应4个字符
char[] chars = s.toCharArray();
stringTemp.append(chars[j]);
backtracking(str, index + 1, stringTemp);
stringTemp.deleteCharAt(stringTemp.length() - 1);
}
}
}
char[] chars = s.toCharArray();
stringTemp.append(chars[j]);
backtracking(str, index + 1, stringTemp);
stringTemp.deleteCharAt(stringTemp.length() - 1);
}
}
}