最长上升子序列(O(nlogn))算法并输出最长上升子序列

  1. 最长上升子序列普通算法
    dp[n]表示以a[n]结尾的最长上升子序列长度
    显然有
    dp[n]=max(dp[n],dp[i]+1) 满足a[i] 实现过程时间复杂度为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];
        }
    }

}

你可能感兴趣的:(算法笔记)