300. 最长上升子序列(Longest Increasing Subsequence)
题解
动态规划
d p [ i ] dp[i] dp[i]表示到当前位置的最长上升子序列的长度。
-
特判,若数组为空,返回0
-
初试化 d p = [ 1 , 1 , ⋯ , 1 ] dp=[1,1,\cdots,1] dp=[1,1,⋯,1],长度为 n n n, d p [ i ] = 1 dp[i]=1 dp[i]=1表示每一位都可以为长度为1的最长上升子序列。
-
遍历 d p dp dp,对于 i i i,遍历区间 [ 1 , n ) [1,n) [1,n):
- 遍历当前位置前的所有位置
j
j
j,遍历区间
[
0
,
i
)
[0,i)
[0,i):
- 若 n u m s [ i ] > n u m s [ j ] nums[i]>nums[j] nums[i]>nums[j],表示满足上升的条件。此时,更新当前位置的最长上升子序列的长度: d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) dp[i]=max(dp[i],dp[j]+1) dp[i]=max(dp[i],dp[j]+1)。
- 遍历当前位置前的所有位置
j
j
j,遍历区间
[
0
,
i
)
[0,i)
[0,i):
-
返回 d p dp dp中的最大值。
复杂度分析
- 时间复杂度: O ( n 2 ) O(n^{2}) O(n2)
- 空间复杂度: O ( n ) O(n) O(n)
Python
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)
Java(待完成)
动态规划+二分查找
定义
t
a
i
l
tail
tail数组,
t
a
i
l
[
i
]
tail[i]
tail[i]保存遍历过程中长度为
i
+
1
i+1
i+1的最长上升子序列的最小尾元素。
t
a
i
l
tail
tail本身要求严格递增。
最终
t
a
i
l
tail
tail的长度就是整个数组最长上升子序列的长度。
解释:
这里长度等于最长上升子序列的长度,但是
t
a
i
l
tail
tail不一定为最长上升子序列。
- 特判,若数组长度小于 2 2 2,返回数组长度。
- 初始化 t a i l = [ n u m s [ 0 ] ] tail=[nums[0]] tail=[nums[0]],表示目前为止长度为1的最长上升子序列的最小尾元素为 n u m s [ 0 ] nums[0] nums[0]
- 遍历数组,对于
i
i
i,遍历区间
[
1
,
n
)
[1,n)
[1,n):
- 若 n u m s [ i ] > t a i l [ − 1 ] nums[i]>tail[-1] nums[i]>tail[−1], t a i l [ − 1 ] tail[-1] tail[−1]表示最后一个元素。若当前的数组元素比 t a i l tail tail的最后一个元素大,将其加入 t a i l tail tail。跳过后续步骤。
- 若不满足,则在 t a i l tail tail中找到第一个比他大的元素,替换掉。使用二分查找,查找替换位置。
- 定义左界 l = 0 l=0 l=0,右界 r = l e n ( t a i l ) − 1 r=len(tail)-1 r=len(tail)−1
- 进入循环,循环条件
l
<
=
r
l<=r
l<=r:
- 定义 m i d = ( l + r ) / / 2 mid=(l+r)//2 mid=(l+r)//2
- 若 n u m s [ i ] < = t a i l [ m i d ] nums[i]<=tail[mid] nums[i]<=tail[mid],更新右界: r = m i d − 1 r=mid-1 r=mid−1
- 否则, l = m i d + 1 l=mid+1 l=mid+1
- 替换, t a i l [ l ] = n u m s [ i ] tail[l]=nums[i] tail[l]=nums[i]
- 返回 t a i l tail tail的长度。
复杂度分析
- 时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
- 空间复杂度: O ( n ) O(n) O(n)
Python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if(not nums):
return 0
n=len(nums)
if(n<2):
return n
tail=[nums[0]]
for i in range(1,n):
if(nums[i]>tail[-1]):
tail.append(nums[i])
continue
l=0
r=len(tail)-1
while(l<=r):
mid=(l+r)//2
if(tail[mid]>=nums[i]):
r=mid-1
else:
l=mid+1
tail[l]=nums[i]
return len(tail)