电话号码的字母组合
原理思想:回溯不需要剪枝,注意回溯结束的条件和恢复现场。由于这道题是求组合的种类,所以需要恢复现场寻找不同的组合。
public List<String> solution(String digits) {
List<String> res = new LinkedList<>();
int length = digits.length();
//判断空值,LeetCode里面的测试用例总有空值
if(length==0)return res;
//hashmap存储电话的对应值
Map<Character, String> map = new HashMap<>();
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");
//StringBuilder是为了找寻每一种可能的结果,其允许对字符串进行多次修改
backtrack(res, map, digits, length, 0, new StringBuilder());
return res;
}
public void backtrack(List<String> res, Map<Character, String> map, String digits, int length, int t, StringBuilder result) {
if(t==length) { //注意回溯结束条件
res.add(result.toString());
}
else {
Character a = digits.charAt(t);
String letter = map.get(a);
int letterLen = letter.length();
for(int i=0; i<letterLen; i++) {
result.append(letter.charAt(i));
backtrack(res, map, digits, length, t+1, result);
//恢复现场,因为要求的是不同的组合,回溯的时候需要去掉下层的结果,也就是result后面的字符
//不然结果是这样[ad, ade, adef, adefbd, adefbde, adefbdef, adefbdefcd, adefbdefcde, adefbdefcdef]
//注意参数是t,不是i,思想:恢复的是最后一个操作的变量
result.deleteCharAt(t);
}
}
}
组合总和
去重思想:搜索时去重,遇到这一类相同元素不计算顺序的问题,我们在搜索的时候就需要 按某种顺序搜索。具体的做法是:每一次搜索的时候设置 下一轮搜索的起点 begin
,如下图:
即:从每一层的第 2 个结点开始,都不能再搜索产生同一层结点已经使用过的 candidate
里的元素。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
List<List<Integer>> res = new LinkedList<>();
int length = candidates.length;
if(length==0)return res; //去除特殊情况
backtrack(res, candidates, new LinkedList<Integer>(), target, 0, 0);
return res;
}
public void backtrack(List<List<Integer>> res, int [] candidates, List<Integer> result, int target, int begin, int sum) {
if (sum==target) {
res.add(new LinkedList<>(result)); //这里需要重新new一个list变量,因为list是动态变化的,如果使用原来的result变量,结果res也会因此改变
return;
}
else {
for(int i=begin; i<candidates.length; i++) {
if(sum+candidates[i]>target)break; //约束条件进行减枝
else {
result.add(candidates[i]);
//由于每一个元素可以重复使用,下一轮搜索的起点依然是 i,这里非常容易弄错,这里的i并不是数的层数所以不需要+1
//因为可以重复使用,所以层数是不确定的,不能用层数t来当做回溯结束的条件
backtrack(res, candidates, result, target, i, sum+candidates[i]);
}
result.remove(result.size()-1);//恢复现场,删除的是最后一个进入list的元素
}
}
}
}
更简洁的backtrack:
public void backtrack(List<List<Integer>> res, int [] candidates, List<Integer> result, int target, int begin, int sum) {
if (sum==target) {
res.add(new LinkedList<>(result));
return;
}
else {
for(int i=begin; i<candidates.length; i++) {
if(sum+candidates[i]<=target){
result.add(candidates[i]);
backtrack(res, candidates, result, target, i, sum+candidates[i]);
result.remove(result.size()-1);
}
}
}
}