递归和动态规划(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]

 

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