问题描述
在算法领域,最长递增子序列(Longest Increasing Subsequence,简称 LIS)是一个经典问题。给定一个整数数组 nums
,我们的任务是找到其中最长严格递增子序列的长度。这里的“子序列”指的是在不改变其余元素顺序的情况下,通过删除(或不删除)数组中的元素得到的新序列。
问题难点
LIS 问题的难点在于如何高效地找到递增子序列。直观的动态规划方法虽然可以解决问题,但时间复杂度较高,为 O(n2)O(n2)。我们寻求一种更优的方法。
算法选择
为了降低时间复杂度,我们采用贪心算法结合二分查找的方法。这种方法的核心是维护一个动态数组 d
,其中每个元素是当前已知最长递增子序列的末尾元素。通过二分查找,我们可以在 O(logn)O(logn) 的时间内确定新元素在 d
中的位置,从而在 O(nlogn)O(nlogn) 的时间内找到 LIS。
算法逻辑
- 初始化一个空的动态数组
d
。 - 遍历输入数组
nums
中的每个元素num
:- 使用
lower_bound
在d
中找到第一个大于等于num
的元素的位置。 - 如果
num
大于d
中的所有元素,将其添加到d
的末尾。 - 否则,替换掉
d
中找到的位置的元素,因为num
可以扩展一个更长的递增子序列。
- 使用
- 返回
d
的长度,即最长递增子序列的长度。
代码实现
#include <iostream>
#include <vector>
#include <algorithm> // for lower_bound
using namespace std;
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> d;
for (int num : nums) {
auto it = lower_bound(d.begin(), d.end(), num);
if (it == d.end()) {
d.push_back(num);
} else {
*it = num;
}
}
return d.size();
}
};
结论
通过贪心算法和二分查找的结合,我们能够有效地解决 LIS 问题,并将时间复杂度降低到 O(nlogn)O(nlogn)。这种方法不仅提高了算法的效率,而且代码实现简洁明了,易于理解和维护。在实际应用中,这种算法可以应用于各种需要找到最长递增子序列的场景,如生物信息学中的序列比对、金融领域的时间序列分析等。