迭代:从上到下来做一件事情,for循环就是迭代的一种。
递归:一般我们认为递归就是迭代的一种。可以重复一直做一件事,直到达到某种条件时,跳出递归。递归的核心思想 1.先找递归出口 2.每次递归方法要做什么。
回溯:其实回溯和递归很相似,都是重复做一件事,区别就是在递归的方法前加“增加操作“,方法后”相应减操作“。
为了更快的了解区别,还是需要例子。LeetCode题
17. 电话号码的字母组合
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
解法思路
- 这是个排列组合问题对吧?这个排列组合可以用树的形式表示出来;
- 当给定了输入字符串,比如:"23",那么整棵树就构建完成了,如下:
从上面图来看我们可以分成两步骤来解决问题:1. 先找数字。2.从数字对应的字母数组中找字母。3.重复步骤1,2。
这样看来我们可以用迭代方法,也可以用递归和回溯来解决问题。但迭代用起来相对复杂,比较乱。
方法1:递归
class Solution {
private String[] letterMap= {
" ", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
///用迭代法思路很混乱,1.选数字 2.选数字中的字母,3 重复1.2步骤
///可以用递归来解决,每次先选数字,然后再选字母
List<String> lists = new ArrayList<>();
private int slen;
public List<String> letterCombinations(String digits) {
slen = digits.length();
if(slen == 0) return lists;
char[] ch = digits.toCharArray();
dfsLetter(ch, 0, "");
return lists;
}
private void dfsLetter(char[] digits, int path, String comLetter){
if(slen == path){//当前路径已经触底,所以把此组合放进数组中
lists.add(comLetter);
return ;
}//digits[path]表示当前的数字,通过数字知道当前数字包含的字母长度
String str = letterMap[digits[path]-'0'];
for(int i=0;i<str.length();i++)
//递归的关键,每次路径加1便增加一个字符
dfsLetter(digits, path+1, comLetter + str.charAt(i));
}
}
注意:26行是递归的出口,递归的核心思想 1.先找递归出口 2.每次递归方法要做什么。
33行 每次递归的目的就是增加路径深度path,添加一个字符str.charAt(i);
但时间复杂度很不理想,原因是String 用 + 来拼接牺牲了内存和时间,可以用回溯来优化
方法2 回溯
class Solution {
private String[] letterMap= {
" ", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
///用迭代法思路很混乱,1.选数字 2.选数字中的字母,3 在次选数字,4选数字中的字母,重复1.2步骤
///可以用递归来解决,每次先选数字,然后再选字母
List<String> lists = new ArrayList<>();
private int slen;
public List<String> letterCombinations(String digits) {
slen = digits.length();
if(slen == 0) return lists;
char[] ch = digits.toCharArray();
dfsLetter(ch, 0, new StringBuilder());
return lists;
}
private void dfsLetter(char[] digits, int path, StringBuilder sb){
if(slen == path){
lists.add(sb.toString());
return ;
}//digits[path]表示当前的数字,通过数字知道当前数字包含的字母长度
String str = letterMap[digits[path]-'0'];
for(int i=0;i<str.length();i++){
sb.append(str.charAt(i));//递归之前添加一个字符
dfsLetter(digits, path+1, sb);
sb.deleteCharAt(path);//递归后剪掉一个字符
}
}
}
回溯的核心我觉得在32行和34行
时间复杂度如下所示: