题目描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
1 | 输入: [10,9,2,5,3,7,101,18] |
说明:
- 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
- 你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
题解
动态规划
算法
- 假设有数组
dp[]
,其中dp[i]
表示到第i
个元素时最长上升子序列(简称 LIS )的长度,此时nums[i]
为 LIS 最后一个元素,即nums[i]
为子序列中最大值。 - 为了计算
dp[i]
,需要把当前元素nums[i]
附加在[0, i)
区间内所有可能的上升子序列后面,并保证附加之后的新子序列仍为上升子序列,即满足条件nums[i] > nums[j]
。 - 计算公式如下:
- LIS 长度为:
复杂度分析
- 时间复杂度:
- 空间复杂度:
代码
1 | // C++ |
二分查找动态规划
算法
- 定义数组
tails[]
,其中tails[i]
表示以这个元素为最大值的上升子序列,则其下标i + 1
就是子序列的长度。保持此数组为递增序列。 - 定义变量
len
,表示目前最长上升子序列长度。 - 从左到右扫描整个数组
nums[]
,对新元素nums[i]
与tails[0...len]
的元素做比较:- 如果
nums[i]
比tails[0...len]
中所有元素小,则对nums[i]
来说,以它为最大值的上升序列长度为1
,则将其放入坐标tails[0]
。 - 如果
nums[i]
比tails[0...len]
中所有元素大,将nums[i]
放入tails[len]
。 表示以nums[i]
为最大值的上升序列长度为len+1
,将len
更新为len+1
。 - 如果
nums[i]
在tails[0...len]
区间内,找到第一个比nums[i]
大的坐标idx
,将nums[i]
放入tails[idx]
。表示以nums[i]
为最大值的上升序列长度仍为idx+1
。
- 如果
复杂度
- 时间复杂度:$O(n \log{n})$ ,二分查找耗时 $\log{n}$,调用 $n$ 次。
- 空间复杂度:$O(n)$
代码
1 | // C++ |
参考链接
- 300. Longest Increasing Subsequence, LeetCode solution.
- Longest Increasing Subsequence | DP-3, GeeksforGeeks.
- Longest Increasing Subsequence Size (N log N), GeeksforGeeks.