最长上升子序列

算法在编程之美上有比较全面的介绍,第一种算法:
时间复杂度 O(n2)
//假设在前i个元素中,最长的递增子序列长度为LIS[i]
//LIS[i+1] = max{1,LIS[k]+1},if for k<=i LIS[i+1]>LIS[k]
//总的时间复杂度为o(n2)

int LIS2(vector<int>&arr){

    vector<int> dp(arr.size(),0);
    //时间复杂度O(n2)
    for(int i=0;i<arr.size();++i){
        for(int j=0;j<i;++j){
            if(arr[i] > arr[j] && dp[j] + 1 > dp[i] ){
                dp[i] = dp[j]+1;
            }
        }
    }
    //时间复杂度 O(n)
    int max_val =0;
    for(int i=0;i<dp.size();++i){
        if(dp[i]>max_val)
            max_val = dp[i];
    }
    return max_val;
}

第二种做法,关键是增加一个记录数组,maxV[i]记录长度为i的最长上升子序列的最大值的最小值。
时间复杂度也是 O(n2)

//长度为1的递增子序列最大元素最小值为MaxV[1]
//长度为2的递增子序列最大元素最小值为MaxV[2]
//我们希望找到前i个元素中的一个递增子序列,使得这个递增子序列的最大元素比array[i+1]小,且长度尽量长。
int LIS(vector<int>& arr)
{
    int currentLength = 1;
    vector<int> maxV; //maxV[i]表示长度为i的最长递增子序列最大值的最小值
    maxV.push_back( ~(1<<31)+1);//最小值
    maxV.push_back(arr[0]); //初始化长度为1时最大值的最小值为第一个元素 
    vector<int> dp(arr.size(),1);//dp[i]记录arr中1到i位中的lis的最长长度
    int len = 1;// LIS长度
    for(int i = 1; i < arr.size(); ++i) 
    {   
        int j;
        for(j= len;j>=0;--j){
            if(arr[i]>maxV[j]){ //长度逐渐减小,因为希望尽可能长
                dp[i] = j+1;   //更新lis的长度 
                break;              
            }
        }

        if(dp[i] > len)//表明最长上升序列长度增加了,即刚才在j=len时就退出了
        {
            ++len;
            maxV.push_back(arr[i]);
            //maxV[++len]= arr[i];
        }else //表明需要替换以前的值
        {
            maxV[j+1] = arr[i];
        }
    }
    return len;
};

第三种算法, 因为存在i<jmaxV[i]<maxV[j], 所以可以采用二分法加速,时间复杂度为 O(nlogn)

// write your code here cpp
#include<iostream>
#include<vector>
using namespace std;
#define LOCAL

//返回比x小的最大的数值的下标
int BinSearch(vector<int>& arr,  int x)
{
    int left = 0, right = arr.size()-1;
    int mid;
    while(left < right -1)
    {
        mid = (left + right) / 2;
        if(arr[mid] < x)
        {
            left = mid ;
        }else
        {
            right = mid-1;
        }
    }
    if(arr[right]<x)
        return right;
    return left;
};

int LIS(vector<int>& arr)
{
    int currentLength = 1;
    vector<int> maxV; //maxV[i]表示长度为i的最长递增子序列最大值的最小值
    maxV.push_back( ~(1<<31)+1);//最小值
    maxV.push_back(arr[0]); //初始化长度为1时最大值的最小值为第一个元素 
    vector<int> dp(arr.size(),1);//dp[i]记录arr中1到i位中的lis的最长长度
    int len = 1;// LIS长度
    for(int i = 1; i < arr.size(); ++i) 
    {   
        int j;
        //将这里换成二分可以降低时间复杂度
        j = BinSearch(maxV,arr[i]);//返回比当前值arr[i]小长度的下标
        dp[i] = j+1; 

        if(dp[i] > len)//表明最长上升序列长度增加了,即刚才在j=len时就退出了
        {
            ++len;
            maxV.push_back(arr[i]);
            //maxV[++len]= arr[i];
        }else //表明需要替换以前的值
        {
            maxV[j+1] = arr[i];
        }
    }
    return len;
};




int main(){

#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif

    int n;
    while(cin>>n){
        int tmp;
        vector<int> arr;
        while(n--){
            cin>>tmp;
            arr.push_back(tmp);
        }
        cout<<LIS(arr)<<endl;
    }   
    return 0;   

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值