遞歸與循環 效率比較( LeetCode 17、電話號碼的字母組合 遞歸實現)

一、問題解答:

1、遞歸爲什麼會出現棧溢出?

大家都知道遞歸的實現原理是通過調用函數本身,在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。遞歸調用,只有走到最後的結束點後函數才能依次退出,而未到達最後的結束點之前,佔用的棧空間一直沒有釋放,如果遞歸調用次數過多,就可能導致佔用的棧資源超過線程的最大值,從而導致棧溢出,導致程序的異常退出。

2、遞歸慢的原因到底是什麼呢?

函數調用的時候,每次調用時要做地址保存,參數傳遞等,這是通過一個遞歸工作棧實現的。具體是每次調用函數本身要保存的內容包括:局部變量、形參、調用函數地址、返回值。那麼,如果遞歸調用N次,就要分配N*局部變量、N*形參、N*調用函數地址、N*返回值。這勢必是影響效率的。

3、用循環效率會比遞歸效率高嗎?

遞歸與循環是兩種不同的解決問題的典型思路。當然也並不是說循環效率就一定比遞歸高,遞歸和循環是兩碼事,遞歸帶有棧操作,循環則不一定,兩個概念不是一個層次,不同場景做不同的嘗試。

4、遞歸使用的棧是什麼樣的一個棧呢?

首先,看一下系統棧和用戶棧的用途。

系統棧(也叫核心棧、內核棧)是內存中屬於操作系統空間的一塊區域,其主要用途爲:

(1)保存中斷現場,對於嵌套中斷,被中斷程序的現場信息依次壓入系統棧,中斷返回時逆序彈出;

(2)保存操作系統子程序間相互調用的參數、返回值、返回點以及子程序(函數)的局部變量。

用戶棧用戶進程空間中的一塊區域,用於保存用戶進程的子程序間相互調用的參數、返回值、返回點以及子程序(函數)的局部變量。

我們編寫的遞歸程序屬於用戶程序,因此使用的是用戶棧。

二、算法比較:

遞歸

優點:代碼簡潔、清晰,並且容易驗證正確性。但是我個人感覺理解起來有困難。

缺點:並不能解決所有的問題。有的問題適合使用遞歸而不是循環。如果使用循環並不困難的話,最好使用循環。

循環

優點:速度快,結構簡單。容易理解

缺點:並不能解決所有的問題。有的問題適合使用遞歸而不是循環。如果使用循環並不困難的話,最好使用循環。

遞歸算法和循環算法總結:

1. 一般遞歸調用可以處理的算法,也通過循環去解決常需要額外的低效處理。

2. 現在的編譯器在優化後,對於多次調用的函數處理會有非常好的效率優化,效率未必低於循環。

3.遞歸和循環兩者完全可以互換。如果用到遞歸的地方可以很方便使用循環替換,而不影響程序的閱讀,那麼替換成循環往往是好的。(例如:求階乘的遞歸實現與循環實現。)

三、舉例:

1、題目:

LeetCode中 17、電話號碼的字母組合   遞歸解

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

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

示例

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

2、思路:

該題採用  深搜 + 遞歸這樣的模式。

首先,可以先構造一個數字與字母相對應的數組,比如,可以寫成這個形式:

num_alp_table = [" ", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]

顯然,num_alp_table中每個元素的索引是對應數字,而元素本身是這個數在手機鍵盤上對應的字母。

寫一個輔助函數helper(),該函數完成對字符的遍歷和拼接

helper()函數一共有5個參數:

digits:用戶輸入的數字字符串

num_alp_table:上面才說過

begin:當前需要處理的數字字符串的位置,比如,輸入“23”,初始時,begin = 0,也就是處理“23”這個字符串的第0位“2”

path:記錄字母組合的字符串

result:結果列表

helper()函數做的事情就是將數字字符串從begin開始到最後依次處理,將每一種可能的字母組合添加到結果列表中。深搜回溯的條件是len(path) == len(digits)(即:在遞歸中,本次遞歸返回的條件是path的長度與輸入字符串長度相同)

3、實現:

使用python 遞歸實現該算法解如下:

class Solution(object):
    def letterCombinations(self, digits):
        """
        :type digits: str
        :rtype: List[str]
        """  
        num_alp_table = [" ", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]
        if len(digits) == 0:
            return []
        path, result = "", []
        begin = 0
        self.helper(digits, begin, path, result, num_alp_table)
        return result
 
    def helper(self, digits, begin, path, result, num_alp_table):
        # if arrive the end of string, return
        if len(path) == len(digits):
            result.append(path)
            return
        # eigodic each value of input keys
        for i in num_alp_table[int(digits[begin])]:
            path += i
            # eigodic the next value
            self.helper(digits, begin + 1, path, result, num_alp_table)
            path = path[:-1]

在此,特別感謝guoziqing506的博客  和 VincentCZW  兩位大神的博客,學習到很多。感恩的小心心~~~

一起加油,狗子們~~~

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