题目描述
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
输入输出
1 | 输入:nums = [10,9,2,5,3,7,101,18] |
基本思路
方法一:动态规划
- 设置
dp[i]
的值代表以nums[i]
结尾的最长子序列长度 - 判断条件:
- 当
nums[i] > nums[j]
时nums[i]可以接在nums[j]后边 此时最长子序列长度dp[j]+1 - 当
nums[i] <= nums[j]
时nums[i]无法接在nums[j]后边 不成立 跳过 - 因此转移方程为 $dp[i] = max(dp[i], dp[j]+1) \quad for \quad j \quad in \quad [0,i)$
- 当
- 初始状态:
dp[i]
中所有元素置1 意思是每个元素都可以单独成为子序列 此时长度为1 - 性能:
- 时复:$O(n^2)$ 遍历计算 dp 列表需 O(N) 计算每个 dp[i] 需 O(n)
- 空复:$O(n)$ dp列表占用线性大小额外空间
方法二:动态规划 + 二分查找
新建一个数组
arr[]
对原序列进行遍历,将每位元素二分插入
arr[]
中。- 如果
arr[]
中元素都比它小,将它插到最后。 - 否则,用它覆盖掉比它大的元素中最小的那个。
- 如果
总之,思想就是让
arr[]
中存储比较小的元素。这样,arr[]
未必是真实的最长上升子序列,但长度是对的。
1 | arr[]的变化: |
- 性能:
- 时复:$O(nlogn)$ 数组 nums 的长度为 n,我们依次用数组中的元素去更新数组,而更新数组时需要进行 $O(logn)$ 的二分搜索,所以总时间复杂度为 $O(nlogn)$
- 空复:$O(n)$ 需要额外使用长度为 n 的数组
java实现
1 | // 方法一 |