回溯算法详解与代码模版-leetcode 17.电话号码的字母组合举例
1. 回溯算法思想
回溯算法其实是一种暴力搜索的思想,提到回溯算法也不能绕开递归,大部分情况下的回溯算法就是一个递归函数。回溯的思想就是先处理逻辑,然后在递归调用自己,在这之后,将之前处理的逻辑撤销掉,这里的撤销会导致的结果就是把原来的结果集的一部分删除,相当于回头重新添加结果,这也是回溯名字的由来。
2. 代码模版
public void backTrack(候选集, 结果集, 临时表) {
// 代码的终止条件 递归出口
if (终止条件) {
// 将临时表放到结果集中
}
// 遍历当前的候选集的所有元素
for (int i = 0; i < 候选集长度; i++) {
// 处理 一般是将元素添加到临时表中
临时表.add(val);
backTrack(候选集, 结果集, 临时表);
// 撤销选择 回溯的根源
临时表.delete(val);
}
}
注意点:
- 回溯算法的递归函数一般是
void返回值 - 一定要注意终止条件,每个具体的题不一样,一般的终止条件就是当前的候选集已经遍历完全了,即没有可以选择的值了
3. 举例说明
采用力扣17.电话号码的字母组合举例说明回溯算法
题目:
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
提示:
0 <= digits.length <= 4digits[i]是范围['2', '9']的一个数字。
思路:
- 保存每个按键所对应的字母,构成候选集
keyMap - 在回溯函数参数传递中增加一个
index用于判断当前是按下的哪个按键,当index按到了最后一个按键,则说明到达了终止条件 - 使用
for遍历每个按键中的每个字母,首先将字母添加到临时表sb中,然后递归到下一个按键,接下来将这个字母从临时表中删除,完成回溯 - 这里的注意点是需要将第几个按键
index和候选集keyMap中的数字对应起来,因为index始终是从0到digits.length的,因此需要找到digits中的index对应的数字,然后将其作为keyMap的索引,即(int)digits.charAt(index) - 50,这里减50是因为将具体的char换成int需要减去48,但这里的数字是从2开始的,因此要减去50
代码如下:
class Solution {
// 结果列表
LinkedList<String> res = new LinkedList();
// 维护一个选择表
char[][] keyMap = new char[][]{{'a', 'b', 'c'}, {'d', 'e', 'f'}, {'g', 'h', 'i'},
{'j', 'k', 'l'}, {'m', 'n', 'o'}, {'p', 'q', 'r', 's'},
{'t', 'u', 'v'}, {'w', 'x', 'y', 'z'}};
public List<String> letterCombinations(String digits) {
// 边界条件
if (digits.equals("")) return new ArrayList();
// 调用回溯函数
backTrack(digits, 0, new StringBuilder());
// 返回结果
return res;
}
private void backTrack(String digits, int index, StringBuilder sb) {
// 如果当前index已经到了最后一个数字 则保存退出
if (index == digits.length()) {
// 将sb的结果 (叶子节点上的) 放到结果集
res.add(sb.toString());
return;
}
// 否则先处理再递归
int i = (int)digits.charAt(index) - 50; // 找到按下的第i个数字
int len = keyMap[i].length; // 记录第i个数字中有几个字母 3 or 4
for (int j = 0; j < len; j++) {
// 把当前字母加到sb中
sb.append(keyMap[i][j]);
// 递归下去 到下一个按键 添加下一个字母
backTrack(digits, ++index, sb);
// 把刚刚添加的字母删除 完成回溯
sb.deleteCharAt(sb.length() - 1);
// 这里减是为了让递归回来的时候 index保持不变,否则会出现数组的溢出
index--;
}
}
}
4. 今日学习
StringBuilder的删除某个索引的函数为sb.deleteCharAt(i)
StringBuilder sb = new StringBuilder();
// 删除sb中的最后一个元素
sb.deleteCharAt(sb.length() - 1);
// StringBuilder的加入元素, 不是add()
sb.append('a');
// StringBuilder的长度, 不是size()
sb.length();

浙公网安备 33010602011771号