动态规划问题(1)

动态规划:求最长递增子序列的的长度

题目描述:

假设有一个数组nums = [1,5,2,4,3],要求找出最长的递增的子序列的长度
,例如nums中最长递增子序列为1,2,4和1,2,3则要求返回长度3

解法一:

思路:可以采用暴力枚举的方法
动态规划问题(1)_第1张图片

nums = [1,5,2,4,3]

def L(nums, i):    # 返回从数组第 i 个数字开始的最长子序列长度
    """Returns the length of longest increasing subsequence starting from i"""
    
    if i == len(nums) - 1:     # last number
        return 1    # 最后一个数字后面没有数字和它构成序列了,直接返回 1
    
    max_len = 1
    for j in range(i + 1, len(nums)):
        if nums[j] > nums[i]:    # 说明 i 后面的数字可以和它构成递增序列
            max_len = max(max_len, L(nums, j) + 1)    # 递归调用,找出最大值
            
    return max_len

def length_of_LIS(nums):
    return max(L(nums, i) for i in range(len(nums)))  # 对每一个 i 依次调用 L 函数,然后选出最大值即可

nums = [1,5,2,4,3]
print(length_of_LIS(nums))

解法一虽然能够帮我们算出答案,不过它最大的问题在于它的时间复杂度,假设数组长度为n,则一共存在2n个子序列,而每一个子序列都需要去遍历一次判断是否是递增序列,则时间复杂度为O(n*2n),这是一个指数级别的算法,最慢的算法之一

解法二:

思路:遍历树中存在大量的重复计算,比如在遍历子序列1,2,4的时候就已经计算过从 4 开始的最大子序列长度,后面遍历1,4的时候又重复计算了一次,为避免重复计算可以将第一次的计算结果保存下来,之后遍历到相同的结点时直接返回前面的计算结果

memo = {}	# 记录下从 i 开始的最长子序列的长度

nums = [1,5,2,4,3]

def L(nums, i):
    """Returns the length of longest increasing subsequence starting from i"""
    
    if i in memo:
        return memo[i]	# 首先检查之前是否保存过这个答案,如果是,直接返回结果,如果不是,再去计算
    
    if i == len(nums) - 1:     # last number
        return 1
    
    max_len = 1
    for j in range(i + 1, len(nums)):
        if nums[j] > nums[i]:
            max_len = max(max_len, L(nums, j) + 1)
            
    memo[i] = max_len	# 保存结果
    return max_len

def length_of_LIS(nums):
    return max(L(nums, i) for i in range(len(nums)))

nums = [1,5,2,4,3]
print(length_of_LIS(nums))

动态规划正是通过避免重复节点的计算,来加速整个计算的过程,由于用到的字典(哈希表)来保存了计算的中间结果,所以动态规划可理解为是用空间换时间的算法

解法三(非递归):

思路:
L(0) = max{ L(1), L(2), L(3), L(4) } + 1
L(1) = max{ L(2), L(3), L(4) } + 1
L(2) = max{ L(3), L(4) } + 1
L(3) = max{ L(4) } + 1
L(4) = 1

从公式可以发现,只要从后往前依次计算,就可以把所有的答案给推算出来

nums = [1,5,2,4,3]

def length_of_LIS(nums):
    n = len(nums)    # 5
    L = [1] * n    # initial value: [1,1,1,1,1]
    
    for i in reversed(range(n)):    # i -> 4,3,2,1,0
        for j in range(i + 1, n):
            if nums[j] > nums[i]:
                L[i] = max(L[i], L[j] + 1)
                
    return max(L)

print(length_of_LIS(nums))

算法的时间复杂度为O(n2),比解法一指数级的时间复杂度要好得多

你可能感兴趣的:(动态规划问题,动态规划,算法,数据结构)