题目描述
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
题目分析
当前题目可以直接使用动态规划来解决,不过时间复杂度为O(n^2),不满足进阶中的要求。
因此,可以使用贪心+二分查找来满足O(nlogn)的时间复杂度。
- 贪心思路:要让上升子序列上升的尽可能的慢,才能找到尽可能长的递增子序列。
- 实现方法
- 维护一个辅助数组tmp,它的每一个元素tmp[i] 的含义是,所有长度为 i+1 的上升子序列的末尾元素中的最小值;
- 使用seq_len表示辅助数组tmp当前的长度,并且在遍历输入数组时,需要维护辅助数组tmp的性质:
- 如果遇到一个比tmp的尾部元素更大的值,说明形成了一个更长的上升序列,则把它追加到tmp的尾部,seq_len加1;
- 如果遇到一个比tmp的尾部元素更小的值,说明发现了某个上升子序列的、更小的末尾元素,需要更新它。因为tmp是有序的,可以使用二分查找目标位置来更新,因此总时间复杂度是 O(nlogn)。
- 最终seq_len就是最长递增子序列的长度。
Code
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int seq_len = 1, size = nums.size();
if (0 == size) {
return 0;
}
vector<int> tmp(size + 1, 0);
tmp[seq_len] = nums[0];
for (int i = 0; i < size; ++i) {
if (nums[i] > tmp[seq_len]) {
tmp[++seq_len] = nums[i];
} else {
int l = 1, r = seq_len, pos = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (tmp[mid] < nums[i]) {
pos = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
tmp[pos + 1] = nums[i];
}
}
return seq_len;
}
};