LeetCode 17 電話號碼的字母組合(字符串、回溯算法Backtracking、遞歸Recursive)

題目要求:

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

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

示例:

輸入:"23"
輸出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

說明:
儘管上面的答案是按字典序排列的,但是你可以任意選擇答案輸出的順序。



 

 

C++代碼:

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        vector<string> nums={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        vector<string> ans;
        if(digits.size()==0){return ans;}
        combinations(digits,nums,ans,"",0);
        return ans;
    }
    
    void combinations(string digits,vector<string> nums,vector<string>& ans,string temp,int start){
        if(start>=digits.size()){ans.push_back(temp); return;}
        int index=digits[start]-'0';
        for(int i=0;i<nums[index].size();++i){
            combinations(digits,nums,ans,temp+nums[index][i],start+1);
        }
        return;
    }
};

 

 

結果(0ms,beat100%,真強大):

 

 

 

解析:

這裏主要用到了回溯算法的思想,首先簡單介紹一下什麼是回溯算法。

回溯算法(Backtracking)實際上是一個類似枚舉的深度優先搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回到上一步還能執行的狀態,嘗試別的路徑,類似於走迷宮一樣。每次退回就是每次的回溯,所以回溯法要保存每一次的狀態。

回溯算法也是一種選優搜索法,按照選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或者達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”
其實就是深度優先搜索的策略,從根結點出發深度搜索。當探索到某一節點時,要先判斷該節點是否包含問題的解,如果包含,就從該節點出發繼續探索下去,若該節點不包含問題的解,則逐層向其祖先節點回溯。

若用回溯法求問題的所有解時,要回溯到根,且跟節點的所有可行的子樹都要已被搜索遍才結束

 

 

首先在原來的letterCombinations函數裏,我們先定義一個字符串類型的vector容器nums,按照電話號碼從0到9存儲相應的字符串,比如0和1是空的,就存"",然後數字2就是"abc",一直到數字9

vector<string> nums={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};

然後再定義一個nums的字符串類型的vector容器ans,用於存放最後要返回的字母組合

vector<string> ans;

下面先做一個初始的判斷,如果要判斷的digits字符串爲空,也就是digits.size()==0,則直接返回當前的ans即可,因爲當前的ans也是空的

if(digits.size()==0){return ans;}

然後就開始調用我們的回溯算法combinations,後面會介紹。

直到找完全部的正確組合,return ans即可。

 

那麼這裏重點就是如何設計回溯算法combinations

combinations函數裏有一些參數介紹下,後面會用到:比如需要進行轉換的digits、前面定義的nums、用於輸出的ans、用於存儲中間轉換好的臨時變量temp以及strat變量。

首先拿start跟digits.size()做比較,因爲後面start會+1,如果start大於等於了digits.size(),那就把轉換好的臨時變量temp給push_back進ans裏,然後return,回溯。

if(start>=digits.size()){ans.push_back(temp); return;}

具體我們通過舉例來說明,會比較清晰,比如就題目的實例:當digits爲"23"時,也就是數字2對應的字母有a、b、c,而數字3對應的字母有d、e、f,所以3×3就有9種組合,也就是"ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"

因爲一開始combinations函數裏temp="",然後start=0

combinations(digits,nums,ans,"",0);

先進行判斷,start=0並沒有大於等於digits.size()爲2,所以不執行{}裏面的內容

if(start>=digits.size()){ans.push_back(temp); return;}

然後我們就要去取得digits裏面每一位的字符所代表的數字,因爲後面需要用它作爲索引,從nums裏面取出該數字所對應的字母。所以這裏定義int變量index=digits[start]-'0',也就是index=digits[0]='2'-'0',然後取了int,就是整數2了

int index=digits[start]-'0';

然後我們就要去取nums裏數字2對應哪些字母,統統遍歷一遍,用一個for循環,i從0開始遍歷到nums[index].size(),也就是nums[2]字符串裏包含的字符的個數,對應"abc",也就是3個

for(int i=0;i<nums[index].size();++i)

然後for循環裏,我們對combinations進行迭代,有2個地方不同,第一個就是這裏的temp要加上nums[index][i],也就是加上當前index和i對應的nums裏面的字母,此視nums[index][i]=nums[2][0],也就是字母a,然後因爲一開始temp爲空,所以這一次temp+nums[index][i]結果就是'a'。然後把它當作新的temp進行下一次的combinations迭代。

還有一個不同就是start要記得+1,因爲這樣我們才行到進行判斷,從而回溯。

combinations(digits,nums,ans,temp+nums[index][i],start+1);

比如,這裏重新迭代combinations,然後temp此時是a了,然後start+1後就變成1了,然後繼續判斷,start=1還是沒超過digits.size()爲2,所以不執行{}裏的內容

if(start>=digits.size()){ans.push_back(temp); return;}

然後這是index就是digits[start]-'0'也就是digits[1]-'0',即'3'-'0',取整數就是數字3

int index=digits[start]-'0';

裏面又嵌套一個循環,nums[3]包括"def"3個字母

for(int i=0;i<nums[index].size();++i)

然後再迭代,這時候temp+nums[index][i]也就是上一次的'a'加上這時的nums[index][i]=nums[3[0]即'd',所以此時的temp就是'ad'了,然後start+1,就變成2了

combinations(digits,nums,ans,temp+nums[index][i],start+1);

因此這時候start+1變成2了,所以滿足下面的條件start>=digits.size(),就把當前的臨時變量temp即'ad'給push_back加入ans裏面,然後return回溯到上一步,也就是沒有加上'd',即temp還是'a',然後start還是1的時候

if(start>=digits.size()){ans.push_back(temp); return;}

之後同理把 "ae", "af"也push_back加入ans裏後,不滿足條件,所以有回溯上一步,即temp還是空,然後for循環i=1,這時候第一個字母就是b,然後繼續一次迭代,得到"bd", "be", "bf",然後c的也同理,得到 "cd", "ce", "cf"

所以最後就得到了9種組合,也就是"ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"

最後return ans即可。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章