【动态规划】子序列问题

子序列问题也是有一堆题目的问题。「代码随想录」带你学透DP子序列问题!300. 最长递增子序列 - 最长递增子序列 - 力扣(LeetCode) (leetcode-cn.com)

300. 最长递增子序列 - 力扣(LeetCode) (leetcode-cn.com)

依次解决:

1.【300】最长递增子序列

300. 最长递增子序列 - 力扣(LeetCode) (leetcode-cn.com)

1.1.动态规划法

老老实实双层循环法。

时间复杂度:O(n^2)

空间复杂度:O(n)

代码如下:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dp=[1 for _ in nums]
        for i in range(len(nums)):
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i]=max(dp[i],dp[j]+1)
        return max(dp)

1.2.贪心法+二分查找法

创建空数组d存储找到的递增子序列,遍历数组nums,如果nums[i]>d[-1],则append;否则,二分查找将nums[i]【插入并替换】到递增子序列中,即寻找最接近nums[i]<=d[j],d[j]=nums[i],d长度是不变的。这么做的结果,d数组中最后的结果并不一定是该序列的真正的最大子序列。

例:

nums:[1,3,6,7,9,4,10,5,6]

d:[1,3,4,5,6,10]

实际最长子序列:[1,3,6,7,9,10]

d数组里留存了寻找最大子序列的记录,如果更换数组起始点,则一定要新的序列比之前的序列长那么数组长度才会变化,因为最后求解的是长度,所以即使这样len(d)仍然是正确答案。

例:

nums:[4,5,6,7,8,1,2]

d:[1,2,6,7,8]

实际最长子序列:[4,5,6,7,8]

nums:[7,8,1,2,3,4,5,6]

d:[1,2,3,4,5,6]

实际最长子序列:[1,2,3,4,5,6]

时间复杂度:O(nlogn)

空间复杂度:O(m)

实现代码如下:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        
        d=[]
        for n in nums:
            if not d or n>d[-1]:
                d.append(n)
            else:
                left,right=0,len(d)-1  
                loc=right      
                while left<=right:
                    mid=(left+right)//2
                    if n<d[mid]:
                        loc=mid
                        right=mid-1
                    elif n>d[mid]:
                        left=mid+1
                    else:
                        loc=mid
                        break
                d[loc]=n
        return len(d)

做完这道题发现,如果用贪心+二分查找,那么要注意的细节在于二分查找,二分查找忠告:不要省略任何一种情况。

被折磨到了,所以贴一下结果。

二分查找的边界详解:详解二分查找算法 - murphy_gb - 博客园 (cnblogs.com)

还有一种方法【动态规划+二分查找】:最长上升子序列 - 最长递增子序列 - 力扣(LeetCode) (leetcode-cn.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值