- 最长上升子序列普通算法
dp[n]表示以a[n]结尾的最长上升子序列长度
显然有
dp[n]=max(dp[n],dp[i]+1) 满足a[i]<a[n],1<=i<n
实现过程时间复杂度为O(n^2)
2.O(nlogn)时间复杂度
思路是用数组lower保存最长上升子序列长度,对于当前元素a[i],若大于数组lower最后一个元素,则插入lower数组。否则,用二分在lower数组找到第一个大于a[i]的元素,并替换a[i]。整体上来说,用了贪心,因为每次的替换是让lower数组每个元素尽可能的小,这样的话就更有可能能在lower数组末尾插入元素。
关键的是,lower数组中保存的元素并不是真正的最长上升子序列,它表示的是最长上升子序列的长度。
那么问题来了?如何求最长上升子序列呢?
我们可以用一个pos2数组,记录一下数组a中的每个元素在lower数组中出现的位置。然后从数组a最后一个元素开始到第一个元素,寻找到最长上升子序列。
具体实现代码如下
int pos2[maxn],answer[maxn];
void LIS_quicker(int arrays[],int lower [],int n){//最长上升子序列nlogn算法+输出最长上升子序列
lower[1]=arrays[1];
int index=1;
pos2[1]=1;
for(int i=2;i<=n;i++) {
if(arrays[i]>=lower[index]) {
lower[++index]=arrays[i];
pos2[i]=index;
}else {
int pos=upper_bound(lower+1,lower+index+1,arrays[i])-lower-1;
lower[pos]=arrays[i];
pos2[i]=pos; //记录原数组中每个元素在 lower数组中出现的位置
}
}
int maxx=999999; //从右往左打印,
for(int i=n;i>=1;i--) {
if(index==0) break;
if(pos2[i]==index&&maxx>a[i]) { //先找第一个在lower数组index位置,再找第一个在lower数组index-1位置直到index=0
answer[index]=i;//保存答案
index-=1;
maxx=a[i];
}
}
}