Medium
题目描述
给你一个整数数组 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-10⁴ <= nums[i] <= 10⁴
**进阶:**你能设计时间复杂度为 O(n log(n)) 的解决方案吗?
解题思路
这道题有两种经典解法:动态规划和贪心+二分查找。
解法1:动态规划 O(n²)
定义 dp[i] 表示以 nums[i] 结尾的最长递增子序列长度。对于每个位置 i,遍历前面所有位置 j,如果 nums[j] < nums[i],则可以将 nums[i] 接在以 nums[j] 结尾的递增序列后面,更新 dp[i] = max(dp[i], dp[j] + 1)。最终答案是 dp 数组中的最大值。
解法2:贪心+二分查找 O(n log n) [推荐]
维护一个数组 tails,其中 tails[i] 表示长度为 i+1 的递增子序列的最小尾部元素。遍历 nums:
- 如果当前元素大于
tails的最后一个元素,直接追加,长度+1 - 否则,用二分查找找到第一个大于等于当前元素的位置并替换
这样做的关键在于:为了让后续有更多元素能够接在序列后面,我们要让每个长度的序列尾部元素尽可能小。
代码实现
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> tails;
for (int num : nums) {
if (tails.empty() || num > tails.back()) {
tails.push_back(num);
} else {
int left = 0, right = tails.size() - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (tails[mid] < num) {
left = mid + 1;
} else {
right = mid;
}
}
tails[left] = num;
}
}
return tails.size();
}
};
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
import bisect
tails = []
for num in nums:
pos = bisect.bisect_left(tails, num)
if pos == len(tails):
tails.append(num)
else:
tails[pos] = num
return len(tails)
public class Solution {
public int LengthOfLIS(int[] nums) {
List<int> tails = new List<int>();
foreach (int num in nums) {
int left = 0, right = tails.Count;
while (left < right) {
int mid = left + (right - left) / 2;
if (tails[mid] < num) {
left = mid + 1;
} else {
right = mid;
}
}
if (left == tails.Count) {
tails.Add(num);
} else {
tails[left] = num;
}
}
return tails.Count;
}
}
var lengthOfLIS = function(nums) {
const tails = [];
for (let num of nums) {
let left = 0, right = tails.length;
while (left < right) {
let mid = Math.floor((left + right) / 2);
if (tails[mid] < num) {
left = mid + 1;
} else {
right = mid;
}
}
tails[left] = num;
}
return tails.length;
};
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 动态规划 | O(n²) | O(n) |
| 贪心+二分查找 | O(n log n) | O(n) |
相关题目
334. Increasing Triplet Subsequence (Medium)
354. Russian Doll Envelopes (Hard)
646. Maximum Length of Pair Chain (Medium)
1671. Minimum Number of Removals to Make Mountain Array (Hard)
1964. Find the Longest Valid Obstacle Course at Each Position (Hard)
2111. Minimum Operations to Make the Array K-Increasing (Hard)
2370. Longest Ideal Subsequence (Medium)
3177. Find the Maximum Length of a Good Subsequence II (Hard)
3176. Find the Maximum Length of a Good Subsequence I (Medium)
3201. Find the Maximum Length of Valid Subsequence I (Medium)
3202. Find the Maximum Length of Valid Subsequence II (Medium)
3409. Longest Subsequence With Decreasing Adjacent Difference (Medium)