Leetcode 300.最长上升子序列(Longest Increasing Subsequence)

Leetcode 300.最长上升子序列

1 题目描述(Leetcode题目链接

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

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

说明:

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

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

2 题解

  本题可以使用动态规划的方法来做,首先定义一个 D P DP DP数组,长度与原数组长度相同, D P [ i ] DP[i] DP[i]表示从数组开始元素到 n u m s [ i ] nums[i] nums[i],包含 n u m s [ i ] nums[i] nums[i]能组成的最长上升子序列的长度,所以 D P [ i ] DP[i] DP[i]的状态转移方程应该为:
i f    n u m s [ i ] > n u m s [ j ] :    D P [ i ] = m a x ( D P [ j ] + 1 , D P [ i ] )     j = 0 ⋯ i − 1 if \ \ nums[i]>nums[j]:\ \ DP[i] = max(DP[j]+1, DP[i])\ \ \ j = 0\cdots i-1 if  nums[i]>nums[j]:  DP[i]=max(DP[j]+1,DP[i])   j=0i1

初始化 D P DP DP数组为全1,这样就是时间复杂度为 O ( n 2 ) O(n^2) O(n2)的动态规划。

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        length = len(nums)
        DP = [1]*length;
        for i in range(1,length):
            for j in range(0, i):
                if nums[i] > nums[j]:
                    DP[i] = max(DP[j]+1, DP[i])
        return max(DP)

  对于进阶要求的时间复杂度,有一个 l o g n logn logn,遍历数组就是 n n n,那么可能会增加一个二分法,这时候要重新定义 D P [ i ] DP[i] DP[i] i + 1 i+1 i+1长度的上升子序列的最小尾部值,那么 D P DP DP数组就是一个升序的数组,使用一个下标记录 D P DP DP数组的最后一个值,初始化 D P DP DP数组全0,遍历原数组时需要判断:

  • 如果当前元素大于 D P DP DP最后一个值,那么需要将这个值加在 D P DP DP数组后;
  • 如果当前元素小于 D P DP DP最后一个值,那么二分查找 D P DP DP,找到第一个比当前元素大的值,并替换之。

对于题目中的示例,此时的 D P DP DP数组变化情况如下:

原数组:nums = [10,9,2,5,3,7,101,18]
DP数组的变化:
DP = [10,0,0,0,0,0,0,0]
DP = [9,0,0,0,0,0,0,0]
DP = [2,0,0,0,0,0,0,0]
DP = [2,5,0,0,0,0,0,0]
DP = [2,3,0,0,0,0,0,0]
DP = [2,3,7,0,0,0,0,0]
DP = [2,3,7,101,0,0,0,0]
DP = [2,3,7,18,0,0,0,0]

最后这个记录的下标加1就是最长上升子序列的长度。

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        length = len(nums)
        DP = [0]*length;
        DP[0] = nums[0]
        retv = 0
        for i in range(1, length):
            if nums[i] > DP[retv]:
                retv += 1
                DP[retv] = nums[i]
            else:
                left, right = 0, retv
                while left < right:
                    mid = (left + right) // 2
                    if DP[mid] >= nums[i]:
                        right = mid
                    else:
                        left = mid + 1
                DP[left] = nums[i]
        return retv + 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值