数组最长递增子序列

求一维数组中的最长递增子序列,也就是着一个标号的序列 b[0],b[1],…,b[m] ( 0 <= b[0] < b[1] < … < b[m] < N ),使得 array[b[0]] < arrary[b[1]] < … < array[b[m]].

Sample思路
分析目标串:1,-1,2,-3,4,-5,6,-7。
使用i来表示当前遍历的位置:
当i=1,显然最长的递增序列为(1),序列长度为1。
当i=2,由于-1<1。因此需要丢弃第一个值重新建立串(-1),当前长度仍为1。
当i=3,由于2>1,2>-1。因此,最长序列为(1,2),(-1,2),长度为2。
依次类推之后,
假设在目标数组array[]的前i个元素中,最长递增子序列的长度为LIS[i]。那么,
LIS[i+1] = max{1,LIS[k]+1},array[i+1]>array[k],for any k<=i
即如果array[i+1]大于array[k],那么第i+1个元素可以接在LIS[k]长的子序列后面构成一个更长的子序列。

Optimize思路
前面的分析是,当考察第i+1个元素的时候,我们不考虑前面i个元素的分布情况。现在我们从另一个角度分析,在考察第i+1个元素的时候同时考虑前面i个元素的情况。
对于前面i个元素的任何一个递增子序列,如果这个子序列的最大元素比array[i+1]小,那么就可以将array[i+1]加在这个子序列后面,构成一个新的递增子序列。
比如当i=4的时候,目标序列为1,-1,2,-3,4,-5,6,-7最长递增子序列有(1,2),(-1,2)。那么只要4>2,就可以把4直接增加到前面的子序列。
因此,我们需要找到前i个元素中的一个递增子序列,使得这个递增子序列的最大元素比array[i+1]小,且长度尽量地长。
仍然假设在数组的前i个元素中,以array[i]为最大元素的最长递增子序列的长度为LIS[i]。
同时,假设:
长度为1的递增子序列最大元素的值为MaxV[1];
长度为2的递增子序列最大元素的值为MaxV[2];
……
长度为LIS[i]的递增子序列最大元素的值为MaxV[LIS[i]]。

#include <stdio.h>

const int INF=100000000;

int Max(int a,int b){
    return a>b?a:b;
}

// O(N^2+N) = O(N^2)
int LIS_Sample(int* array,int n){
    int *LIS = new int[n];  //前i个元素中,最长递增子序列的长度为LIS[i]
    int Max_LIS = 1;
    for(int i=0;i<n;i++){
        LIS[i] = 1;
        for(int j=0;j<i;j++){   //前面最长的序列
            if(array[i] > array[j])
                LIS[i] = Max(LIS[i],LIS[j]+1);
        }
        Max_LIS = Max(Max_LIS,LIS[i]);
    }
    delete LIS;
    return Max_LIS;
}

int LIS_Optimize(int* array,int n){
    int *MaxV = new int[n+1];   //保存长度为i的递增子序列最大元素的值
    int *LIS = new int[n];      //以array[i]为最大元素的最长递增子序列的长度为LIS[i]
    int Max_LIS = 1;            //数组最长递增子序列的长度  
    MaxV[0] = -INF;
    MaxV[1] = array[0];

    //初始化最长递增序列的信息;
    for(int i=0;i<n;i++) 
        LIS[i] = 1;

    for(int i=1;i<n;i++){
        //遍历历史最长递增序列信息
        int j=0;
        for(int j = Max_LIS; j>=0;j--){
            if(array[i] > MaxV[j]){
                LIS[i] = j+1;
                break;
            }
        }

        //如果当前最长序列大于最长递增序列长度,更新最长信息
        if(LIS[i] > Max_LIS){
            Max_LIS = LIS[i];
            MaxV[LIS[i]] = array[i];
        }
        else if(MaxV[j]<array[i] && array[i] < MaxV[j+1]){
            MaxV[j+1] = array[i];
        }
    }
    /*
    //打印序列
    stack<int> s;
    s.push(array[max_index]);
    int len = 1;
    int last = array[max_index];
    for(int i=max_index-1;i>=0;i--){
        if(LIS[i] == (Max_LIS-len) && array[i] < last)
            s.push(array[i]),len++;
    }
    while(!s.empty()){
        printf("%d ",s.top());
        s.pop();
    }
    printf("\n");
    */
    return Max_LIS;
}

int main(){
    int a[8] = {1,-1,2,-3,4,5,6,-7};
    printf("Sample calculate the longest length is %d\n",LIS_Sample(a,8));
    printf("Optimize calculate the longest length is %d\n",LIS_Optimize(a,8));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值