Leetcode[300. 最长递增子序列]

0. 题目

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

1. 基本思路

我们开一个长度为len(nums)的数组dp,将dp中所有元素初始化为 1 (子序列的最小长度为1),dp[i] 表示以第 i 个元素为结尾的最大递增子序列的长度。
则我们可以写出状态转移方程,对于dp[i]:
dp[i] = max(dp[j])+1 nums[i]>nums[j] 且 0<=j<i
之后利用递推方程依次求出dp所有值,之后输出max(dp)即可
空间复杂度O(n),时间复杂度O(n^2)

2. 代码

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
    	#一定要记得判断下输入是否为空数组,有些题目会设置奇怪的测试样本
    	if not nums:
    		return 0
        n=len(nums)
        dp=[1]*n
        for i in range(n):
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i]=max(dp[i],dp[j]+1)
        return max(dp)   

3. 进阶思路

常规思路中,遍历一遍nums(dp)需要O(n)的时间复杂度,求解每个dp[i]需要O(n)的时间复杂度,总的时间复杂度为O(n^2),遍历nums不可避免,即其对应的O(n)的时间复杂度无法优化,我们只能从dp[i]入手。
为了使序列最长,我们希望序列增大的尽可能慢。可以开辟一个数组tails,tails[i]用来存储长度为i+1的递增序列的末位数值(即序列中的最大值),为了满足序列增大的尽可能慢这个条件,我们希望递增子序列的末尾数值尽可能小。同时,由tails的定义可知,tails一定递单调递增。对于递增/递减的序列,可以采用二分搜索。
遍历nums中的每一个num,若num>tails中的所有值,则递增子序列的长度加一,并将num加入tails中。若tails中有大于num的数,找到最小的大于num的数,并将这个数变更为num。
这样,遍历完整个数组后即可得到问题的解。
遍历nums的时间复杂度为O(n),二分搜索的时间复杂度为O(logn),总的时间复杂度为O(nlogn)

4. 代码

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums)<=1:
            return len(nums)
        tails,res=[0]*len(nums),0
        for num in nums:
            left,right=0,res
            while left<right:
                mid=(left+right)//2
                if tails[mid]<num:
                    left=mid+1
                else:
                    right=mid
            tails[left]=num
            if left==res:
                res+=1
        return res

5. 补充

二分查找代码:

def binarySearch(A,left,right,x): #在A的[left,right]区间查找x
	while left<=right:
		mid=(left+right)//2
		if A[mid]==x:
			return mid
		elif A[mid]>x:
			right=mid-1
		else:
			left=mid+1
	return -1

找到数组中第一个大于等于x的元素的idex:

def lower_bound(A,left,right,x): #在A的[left,right]区间查找x
	while left<right:
		mid=(left+right)//2
		if A[mid]>=x:
			right=mid
		else:
			left=mid+1
	return left

找到数组中第一个大于x的元素的idex:

def upper_bound(A,left,right,x): #在A的[left,right]区间查找x
	while left<right:
		mid=(left+right)//2
		if A[mid]>x:
			right=mid
		else:
			left=mid+1
	return left
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值