遞歸和動態規劃(python)

        暴力遞歸: 1,把問題轉化爲規模縮小了的同類問題的子問題 2,有明確的不需要繼續進行遞歸的條件(base case) 3,有當得到了子問題的結果之後的決策過程 4,不記錄每一個子問題的解
        動態規劃: 1,從暴力遞歸中來 2,將每一個子問題的解記錄下來,避免重複計算 3,把暴力遞歸的過程,抽象成了狀態表達 4,並且存在化簡狀態表達,使其更加簡潔的可能

1、求n!的結果

        想法是轉換成fact(n)=n*fact(n-1)

# 求n!,時間複雜度爲O(logN)
def factorial(n):
    if n == 1:
        return 1
    else:
        return n*factorial(n-1)

if __name__ == '__main__':
    n = int(input())
    print(factorial(n))

2、漢諾塔問題   打印n層漢諾塔從最左邊移動到最右邊的全部過程

A,B,C三個圓柱,分別爲初始位,過渡位,目標位,設A柱爲初始位,C位爲最終目標位

(1)將最上面的n-1個圓盤從初始位移動到過渡位

(2)將初始位的最底下的一個圓盤移動到目標位

(3)將過渡位的n-1個圓盤移動到目標位

對於遞歸算法中的嵌套函數f(n-1)來說,其初始位,過渡位,目標位發生了變化
 

# 漢諾塔問題
def Hanoi(n,from1,help,to):
    if n == 1:
        print(from1, '-->', to)
    else:
        Hanoi(n-1, from1, to, help)
        print(from1, '-->', to)
        Hanoi(n-1, help, from1, to)
if __name__ == '__main__':
    n = int(input())
    Hanoi(n,'A','B','C')

3、打印一個字符串的全部子序列,包括空字符串

每次都選擇是否將當前字符加入子序列

# 求字串的所有子序列
def printAllSubsquence(test, i, res):
    if i == len(test):
        print(res)
        return
    printAllSubsquence(test, i+1, res)              # 當前位置字符不加入
    printAllSubsquence(test, i+1, res + test[i])    # 當前位置字符加入
if __name__ == '__main__':
    s = input()
    res = ""
    printAllSubsquence(s, 0, res)

4、打印一個字符串的全部排列

此題跟https://leetcode-cn.com/problems/permutations/是差不多的。故下面的代碼是數字的全排列,主要思路是先排好第一個,後面的n-1個數繼續全排列。

class Solution(object):
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        ans = []
        if len(nums)==0 or len(nums)==1:
            return [nums]
        for index,item in enumerate(nums):
            res = nums[:index] + nums[index+1:]
            for j in self.permute(res):
                ans.append(j+[item])
        return ans

進階版:給定一個可包含重複數字的序列,返回所有不重複的全排列。題目地址:https://leetcode-cn.com/problems/permutations-ii/ 主要是,加入一個去重的判斷:

# 全排列,有重複數字
def permuteUnique(nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    """
    ans = []
    if len(nums) == 0 or len(nums) == 1:
        return [nums]
    for index, item in enumerate(nums):
        res = nums[:index] + nums[index + 1:]
        for j in permuteUnique(res):
            ans.append(j + [item])
    return ans

5、最小路徑和

給你一個二維數組,二維數組中的每個數都是正數,要求從左上 角走到右下角,每一步只能向右或者向下。沿途經過的數字要累 加起來。返回最小的路徑和。題目地址:https://leetcode-cn.com/problems/minimum-path-sum/

以這道題爲例子,演示怎麼從暴力遞歸改成動態規劃:

首先是暴力遞歸,從右下角開始,這個數只能往左或者往上走,查看每一步往右或者往上最小的,加起來。

class Solution(object):
    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        return self.process1(grid,len(grid)-1,len(grid[0])-1)
    def process1(self,grid,i,j):
        res = grid[i][j]
        if i==0 and j==0:
            return res
        if i==0 and j!=0:
            return res + self.process1(grid,i,j-1)
        if j==0 and i!=0:
            return res + self.process1(grid,i-1,j)
        return res + min(self.process1(grid,i-1,j), self.process1(grid,i,j-1))

但如果把這個答案放入leetcode中,很顯然會超出時間限制,因此要將暴力遞歸改成動態規劃:

class Solution(object):
    def minPathSum(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        m,n = len(grid),len(grid[0])
        dp = [[0 for _ in range(n)] for _ in range(m)]
        dp[0][0] = grid[0][0]
        for i in range(1,m):
            dp[i][0] = dp[i-1][0] + grid[i][0]
        for j in range(1,n):
            dp[0][j] = dp[0][j-1] + grid[0][j]
        for i in range(1,m):
            for j in range(1,n):
                dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i][j]
        return dp[m-1][n-1]

 

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