迭代:从上到下来做一件事情,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行
时间复杂度如下所示: