【力扣日记】300 最长上升子序列 | DP | 二分查找

题目描述

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

示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。

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

算法思路

DP

动态规划。

1、子序列:不要求连续只要保证元素前后顺序一致即可;
2、上升:这里的“上升”是“严格上升”,元素不能相等;

状态转移方程:
遍历到索引是 i 的数的时候,我们应该把索引是 [0, ... ,i - 1] 的 DP数组(序列长度)都看一遍,如果当前的数 nums[i] 大于之前的某个数,那么 nums[i] 就可以接在这个数后面形成一个更长的序列 。把前面的 i 个数都看了, i对应的最长序列即比当前数nums[i]要小的那些序列长里,找最大的,然后加 1 。

算法:

        n=len(nums)
        if n<2:return n
        dp=[1]*n
        for i in range(1,n):
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i]=max(dp[i],dp[j]+1)
        return max(dp)

双重遍历,时间复杂度为 O ( N 2 ) O(N^2)

DP+二分查找

妈耶,差点就看不懂答案了,哭。

这里创建维护一个DP数组,定义为:下标i表示序列长为i+1的序列,dp[i]表示这个长度的序列的最小尾数。

遍历数组nums。
如果当前值K-2大于所有的dp值,那么表示当前的最大序列i可以+1,最新的序列长度为i+1,尾数就是当前值K-2;
再然后出现一个值K,大于所有的dp值,那么表示最大序列长度为i+2,尾数就是当前值K;
再然后出现一个值为K+1,也大于所有dp值,那么有最新序列长度为i+3,尾数为K+1;
下一个数是K-1,k-2<k-1<k,所以这个数可以接在尾数为K-2的序列后面,即更新长度为i+2的序列的尾数。

class Solution(object):
    def lengthOfLIS(self, nums):
        tails, res = [0] * len(nums), 0
        for num in nums:
            i, j = 0, res
            while i < j:
                m = (i + j) // 2
                if tails[m] < num: i = m + 1 
                # 如果要求非严格递增,将此行 '<' 改为 '<=' 即可。
                else: j = m
            tails[i] = num
            if j == res: res += 1
        return res

执行用时 :28 ms, 在所有 Python 提交中击败93.27%的用户
内存消耗 :12.8 MB, 在所有 Python 提交中击败了5.45%的用户

比第一种方法快了30倍。
时间复杂度为一层遍历 O ( N ) O ( l o g n ) = O ( N l o g n ) O(N)*O(log_n)=O(Nlog_n)

参考

给大佬点一百个赞

发布了210 篇原创文章 · 获赞 20 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Heart_for_Ling/article/details/104859027