理解迭代,遞歸,回溯算法思想


迭代:從上到下來做一件事情,for循環就是迭代的一種。

遞歸:一般我們認為遞歸就是迭代的一種。可以重復一直做一件事,直到達到某種條件時,跳出遞歸。遞歸的核心思想  1.先找遞歸出口  2.每次遞歸方法要做什么。

回溯:其實回溯和遞歸很相似,都是重復做一件事,區別就是在遞歸的方法前加“增加操作“,方法后”相應減操作“

為了更快的了解區別,還是需要例子。LeetCode題

17. 電話號碼的字母組合

給定一個僅包含數字 2-9 的字符串,返回所有它能表示的字母組合。

給出數字到字母的映射如下(與電話按鍵相同)。注意 1 不對應任何字母。

解法思路

  • 這是個排列組合問題對吧?這個排列組合可以用樹的形式表示出來;
  • 當給定了輸入字符串,比如:"23",那么整棵樹就構建完成了,如下:

    算法過程.png

從上面圖來看我們可以分成兩步驟來解決問題: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行

時間復雜度如下所示:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM