LeetCode 300. 最长递增子序列
题目描述
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
提示:
- 1 <= nums.length <= 2500
- -104 <= nums[i] <= 104
进阶:
- 你可以设计时间复杂度为 O(n2) 的解决方案吗?
- 你能将算法的时间复杂度降低到 O(n log(n)) 吗?
解题思路
思路一:
首先找出以 nums[i] 为末尾元素的最长递增子序列,然后取最大值即为全局最长递增子序列。
时间复杂度 O(n^2) 空间复杂度 O(n)。
思路二:
从1开始,找出长度为k的递增子序列的最小末尾元素值,然后就可得知自增子序列最大长度。
为何是最小末尾元素值?是为了让递增子序列可以尽可能最长。
时间复杂度 O(n^2) 空间复杂度 O(n)。
思路三:
继续沿着思路二思考,我们观察维护的这个最小末尾元素序列,可以发现其必然是递增序列。此处可用反证法证明。
有了这个发现之后,我们就可以用二分查找来优化我们算法的时间复杂度了。
时间复杂度 O(nlogn) 空间复杂度 O(n)。
参考代码
解法一:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
size_t n = nums.size();
vector<int> len(n, 1); // LIS end up with nums[i]
for (size_t i = 0; i<n; i++) {
for (size_t j = 0; j<i; j++) {
if (nums[j] < nums[i]) {
len[i] = max(len[i], len[j] + 1);
}
}
}
int maxlen = 0;
for (int x : len) {
if (x > maxlen) {
maxlen = x;
}
}
return maxlen;
} // O(n^2) time O(n) space
};
解法二:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> tail;
tail.push_back(INT32_MIN); // guard, for len=1 update
for (int x : nums) {
for (int64_t i = tail.size() - 1; i >= 0; i--) {
if (tail[i] < x) {
if (i == tail.size() - 1) {
tail.push_back(x);
} else {
tail[i+1] = min(tail[i+1], x);
}
}
}
}
return tail.size() - 1;
} // O(n^2) time O(n) space
};
这里用了一个哨兵,用来解决长度为1的递增子序列的最小末尾值更新,使其和长度大于1的使用相同逻辑来维护。
解法三:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> tail;
tail.push_back(INT32_MIN); // guard, for len=1 update
for (int x : nums) {
auto it = lower_bound(tail.begin(), tail.end(), x);
if (it == tail.end()) {
tail.push_back(x);
} else {
*it = x;
}
}
return tail.size() - 1;
} // O(nlogn) time O(n) space
};
还有一种经典写法更精简,充分利用了 lower_bound 返回值是 大于或等于 x 的第一个元素位置 这一特点:
int lengthOfLIS(vector<int>& nums) {
size_t n = nums.size();
vector<int> tail(n, INT32_MAX);
for (int x : nums) {
*lower_bound(tail.begin(), tail.end(), x) = x;
}
return lower_bound(tail.begin(), tail.end(), INT32_MAX) - tail.begin();
} // O(nlogn) time O(n) space