迭代:從上到下來做一件事情,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行
時間復雜度如下所示: