300. 最长上升子序列(动态规划or动态规划+二分查找)

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 O(n2) 。

进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

中等
【分析】动态规划:状态+状态转移方程。
dp[i]为以num[i]结尾的前面子串{num[0],...,num[i]}的最长上升子串的长度。

  • 初始化dp[i]=1
  • dp[i]=max{dp[i],dp[j]+1} for j in range(i) if dp[i]>dp[j]
  • O ( n 2 ) O(n^2) O(n2)
class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums)==0:
            return 0
        dp=[1 for i in range(len(nums))]
        for i in range(1,len(nums)):
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i]=max(dp[i],dp[j]+1)
        return max(dp)

【分析】动态规划+二分查找

  • O ( n l o g n ) O(nlogn) O(nlogn)
  • 在dp中装入nums中的值,用dp的下标表示其值在当前位置下的最长上升子串长度-1(为什么要-1,因为下标从0开始的)。
    • 如果dp[-1]<nums[i] : nums[i]直接加入dp的末尾;
    • 否则,在dp中利用二分查找,并且把dp[j]>=nums[i] and dp[j-1]<nums[i]的j这个位置的值替换nums[i]
    • 如此就把动态规划中内循环的时间复杂度从 O ( n ) O(n) O(n)降低到 O ( l o g n ) O(logn) O(logn)了,不难想到dp数组中的数都是严格升序的。
  • resdp变化,取最长上升子串长度的最大值。
  • e.g. nums=[1,3,6,7,9,4,10,5,6]
    • dp=[1],res=1
    • dp=[1,3],res=2
    • dp=[1,3,6],res=3
    • dp=[1,3,6,7],res=4
    • dp=[1,3,6,7,9],res=5
    • dp=[1,3,4,7,9],res=5,当nums[i]=4,要把4 insert到dp中去,利用二分法找到应该insert的位置。
    • dp=[1,3,4,7,9,10],res=6
    • dp=[1,3,4,5,9,10],res=6
    • dp=[1,3,4,5,6,10],res=6

注: # res既是当前i为止的最长升序子串的长度,也是dp中计入二分查找的升序数组的末尾下标。

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dp=[0 for i in range(len(nums))]
        res=0
        for i in range(len(nums)):
            l,r=0,res  
            while l<r:
                mid=(l+r)>>1                    
                if dp[mid]>=nums[i]:
                    r=mid
                else:
                    l=mid+1            
            dp[l]=nums[i]
            res=max(res,l+1)
        return res

注:进去while直接利用二分查找,巧妙十分啊!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值