题目描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
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.