在题解区找到3个比较好的方法,记录一下。
方法一:暴力动态规划(超时)
具体做法:
遍历两层,前面的指针i记录元素,后面指针j比较与前面的值,若是前面大,理论上dp[i]需要加上1,但是需要用dp[i] < dp[j] + 1判断是否是需要这个j,若是还有更大的就不需要这个j。但是因为两层遍历,时间复杂度很高,会超时。
class Solution {
public:
vector<int> LIS(vector<int>& arr) {
vector<int> dp(arr.size(), 1); //设置数组长度大小的动态规划辅助数组
int max = 0;
for(int i = 1; i < arr.size(); i++){
for(int j = 0; j < i; j++){
if(arr[i] > arr[j] && dp[i] < dp[j] + 1) {
dp[i] = dp[j] + 1; //i点比j点大,理论上dp要加1
//但是可能j不是所需要的最大的,因此需要dp[i] < dp[j] + 1
max = max > dp[i] ? max : dp[i]; //找到最大长度
}
}
}
vector<int> res(max);
for(int i = dp.size() - 1, j = max; j > 0; i--){ //逆向找
if(dp[i] == j){ //该点的长度恰好等于res的第j位,即找到了
j--;
res[j] = arr[i];
}
}
return res;
}
};
复杂度分析:
时间复杂度:O(n^2),两层for循环
空间复杂度:O(n),辅助数组的大小
方法二:二分法动态规划
具体做法:
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
/**
* return the longest increasing subsequence
* @param arr int整型vector the array
* @return int整型vector
*/
vector<int> LIS(vector<int>& arr) {
// write code here
// 第一步:利用贪心+二分求最长递增子序列长度
vector<int> res;
vector<int> maxLen;
if (arr.size() < 1) return res;
res.emplace_back(arr[0]); // 注:emplace_back(val)作用同push_back,效率更高
maxLen.emplace_back(1);
for (int i = 1; i < arr.size(); ++i) {
if (arr[i] > res.back()) {
res.emplace_back(arr[i]);
maxLen.emplace_back(res.size());
} else {
// lower_bound(begin, end, val)包含在<algorithm>中
// 它的作用是返回有序数组begin..end中第一个大于等于val的元素的迭代器
int pos = lower_bound(res.begin(), res.end(), arr[i]) - res.begin();
res[pos] = arr[i];
maxLen.emplace_back(pos+1);
}
}
// 第二步:填充最长递增子序列
for (int i = arr.size()-1, j = res.size(); j > 0; --i) {
if (maxLen[i] == j) {
res[--j] = arr[i];
}
}
return res;
}
};
或者使用动态规划
class Solution {
public:
int biSearch(int x, vector<int>& dp){ //二分查找函数
int left = 0, right = dp.size(), mid;
while(left <= right){
mid = (right + left) / 2;
if(dp[mid] >= x)
right = mid - 1;
else
left = mid + 1;
}
return left;
}
vector<int> LIS(vector<int>& arr) {
if(arr.size() == 0){ //处理特殊情况
vector<int> res;
return res;
}
vector<int> len; //设置数组长度大小的动态规划辅助数组
vector<int> dp;//用于二分划分的辅助数组
dp.push_back(arr[0]);
len.push_back(1);
for(int i = 1; i < arr.size(); i++){
if(arr[i] > dp[dp.size() - 1]) {
dp.push_back(arr[i]);
len.push_back(dp.size());
}
else{
int t = biSearch(arr[i], dp); //二分查找,找到第一个大于arr[i]的dp位置
dp[t] = arr[i];
len.push_back(t + 1);
}
}
int j = dp.size();
vector<int> res(j);
for(int i = len.size() - 1; j > 0; i--){ //逆向找
if(len[i] == j){ //该点的长度恰好等于res的第j位,即找到了
j--;
res[j] = arr[i];
}
}
return res;
}
};