题目描述
Given an integer array nums
, return the length of the longest strictly increasing subsequence.
Example 1:
1 | Input: nums = [10,9,2,5,3,7,101,18] |
Example 2:
1 | Input: nums = [0,1,0,3,2,3] |
Example 3:
1 | Input: nums = [7,7,7,7,7,7,7] |
Constraints:
1 <= nums.length <= 2500
-10^4 <= nums[i] <= 10^4
Follow up: Can you come up with an algorithm that runs in O(n log(n))
time complexity?
解题方法
动态规划
定义一个 dp
数组,dp[i]
代表 nums
以 nums[i]
为结尾的最长递增子序列长度。
状态转移方程:
- 设 $j∈[0,i)$,即每一轮计算新的 $dp[i]$ 时,需要遍历 $[0,i)$ 列表区间
- 当 $nums[i] > nums[j]$ 时,表明 $nums[i]$ 可以接到递增序列的末尾,此时 $dp[i]=dp[j]+1$。
- $nums[i]<=nums[j]$ 时,跳过,因为此时 $nums[i]$ 不能够作为递增子序列的末尾。
- 转移方程:$dp[i]=max(dp[i],dp[j]+1),j∈[0,i),i∈[0,n]$
- 初始化
dp
为值为 1 的数组,因为每个元素都可以单独成为一个子序列。
1 | class Solution: |
时间复杂度:$O(n^2)$
空间复杂度:$O(n)$
使用二分查找降低时间复杂度
将整个 dp
数组设计为一个排序列表,在需要计算最长递增子序列时,使用二分查找遍历 dp
数组,找到需要插入的下标位置,之后进行判断。
- **初始化一个空的列表
sub
**:- 这个列表用于保存当前找到的最长递增子序列的末尾元素。
- **遍历数组
nums
**:- 对于每个元素
num
,使用二分查找来确定num
在sub
中的位置。 - 如果
num
大于sub
中的所有元素(即num
是新的最大值),就将其添加到sub
的末尾。 - 如果
num
不是最大的,找到它在sub
中应该插入的位置,并替换掉该位置的元素。这样可以保持sub
的递增性质,并为后续元素提供更大的可能性。
- 对于每个元素
- 返回
sub
的长度:sub
的长度即为最长严格递增子序列的长度。
1 | class Solution: |
时间复杂度:$O(nlogn)$
空间复杂度:$O(n)$