300. 最长上升子序列

Q:

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

示例:

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

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

A:

1.常规dp

#
# @lc app=leetcode.cn id=300 lang=python3
#
# [300] 最长上升子序列
#
class Solution:
    def lengthOfLIS(self, nums) -> int:
        l=len(nums)
        if l==0 or l==1:
            return l
        dp=[1 for i in range(l)]
        #dp[i]表示截止到i的最长上升子序列长度(包括i本身)
        for limit in range(l):
            i,_max=limit-1,0
            while i>=0:
                _max=max(dp[i],_max) if nums[i]<nums[limit] else _max
                i-=1
            dp[limit]=_max+1
        # print(dp)
        return max(dp)
# print(Solution().lengthOfLIS([10,9,2,5,3,7,101,18]))

2.dp+二分
写不出来,看评论区大佬的,思路是既然要nlogn,必然要二分,二分就必然要有序序列才能二分,所以dp数组存目前为止的最长上升序列,对于nums的当前考察元素nums[i],在dp上升序列里找比其大的最小值,替换为nums[i],当然如果没有比nums[i]大的就直接添加到上升序列末尾。
关键点是向中间插入的情况:如已存的上升序列为2,3,6,8,这时候nums[i]为5,按上面思路应当是覆盖掉6,即变为2,3,5,8。这样覆盖之后并没有改动上升序列的长度,还是4。这样覆盖的原因是5比6更小,理论上可以和更多的数字形成上升序列(如后面又有个6),那么就把6再把8覆盖。不然6和6是构不成上升序列的,反正确实有点不好理解,属于比较巧妙的非常规思路。

class Solution:
    def lengthOfLIS(self, nums) -> int:
        l=len(nums)
        if l==0 or l==1:
            return l
        res=0
        tails=[0 for i in range(l)]
        #tails[i]记录截止到i的最长上升子序列末尾元素值
        for num in nums:
            le,ri=0,res
            while le<ri:
                mi=le+(ri-le)//2
                if tails[mi]<num:
                    le=mi+1
                else:
                    ri=mi
            tails[le]=num
            if le==res:
                res+=1
            print(tails,res)
        return res
posted @ 2019-09-14 13:57  NeoZy  阅读(98)  评论(0编辑  收藏  举报