编程之美5:求数组中最长递增子序列

最近楼楼被男朋友带着玩dota,有点上瘾,终于在昨天晚上作出了一个重大的决定,shift+delete删掉warIII文件夹,从此退出dota的明争暗斗。不过最近看男票已经将战场从11转到了topcoder,嗯,这是个好现象,希望楼楼也能跟着玩儿起来。

理想是美好的,唉,可是楼主还在编程之美的初级阶段啊。话不多说了,希望自己加油加油再加油!!(^o^)/~

今天要看的一道题目是求数组中最长递增子序列。

题目简介:

写一个时间复杂度尽可能低的程序,求一个一维数组(N)个元素中的最长递增子序列的长度。例如:在序列1, -1, 2, -3, 4, -5, 6, -7的最长递增子序列的长度为4,这个子序列为1, 2, 4, 6.

注意:题目只要求求长度,不一定要求出这个递增序列。

解法分析:

在这里,要记住一个特别重要的点(对于像楼主这种渣渣),那就是满足无后效性可以使用动态递归来解决。什么叫无后效性,比如1, -1, 2, -3, 4, -5, 6, -7这个序列,当我们找到-1,2,当我们遍历到元素4的时候,只需要比较4和2的关系,如果4比2大,那就可以加进去。而2之前的元素对于我们做决断没有任何影响,这就叫做“无后效性”。所以我们的解法是:

对于前面i个元素的任何一个递增子序列,如果这个子序列的最大元素比array[i+1]小,那么就可以将array[i+1]元素加在这个子序列后面,构成一个新的递增子序列。

详情请见代码,算法复杂度O(N^2),代码中我有非常详细的解释,有什么不明白的,请留言。

#include <iostream>  
#include <vector>
using namespace std;  

int LIS (int *pArray, int len);
int getMinOfArray (int *pArray, int len);

int main()  
{  
    int a[] = {2, -1, 2, -3, 4, 3, 1, 2, -7, 3, 4, 5, 6, 7, 8, 9};
    int len = sizeof(a) / sizeof(int);
    vector<int> longestSubSequence;

    cout << "最大递增序列的长度为" << LIS(a, len) << endl;
    system("pause");
} 


int LIS (int *pArray, int len)
{
    int *MaxV = new int[len + 1]();
    MaxV[1] = pArray[0];
    MaxV[0] = getMinOfArray(pArray, len) - 1;
    int *LIS = new int[len]();

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

    int nMaxLis = 1;

    for (int i = 0; i < len; i++)
    {
        int j;

        //下面代码的作用是递归,看有没有比最长递增序列的最大元素的最小值大,如果有的话,那么就可以直接加在后面了。然后最长递增序列的个数加1
        //比如,当i = 4, nMaxLis = 2,时,MaxV[2] = 2, a[i] = 4, 4 > 2, 那么4可以加在2后面,递增序列的元素个数加1。找到之后,直接break.
        for (j = nMaxLis; j >= 0; j--)
        {
            if (pArray[i] > MaxV[j])
            {
                LIS[i] = j + 1;
                break;
            }
        }

        //如果i处的最大递增序列,比原来的nMax大的话,那么就更新啦。并且最长递增序列的最大元素的最小值更新为当前元素
        if (LIS[i] > nMaxLis)
        {
            nMaxLis = LIS[i];
            MaxV[LIS[i]] = pArray[i];
        }

        //如果没有比原来的最大长度还长的话,那么就有可能需要更新本长度最大元素的最小值了。比如,nMaxLis = 3的时候,前面我们找到的序列是{1, 2, 4}或者{-1, 2, 4}
        //当 i = 5时,j = 2时跳出循环。Lis[5] = 3 <= nMaxLis, 当时3比4小,所以nMaxLis = 3时最大元素的最小值由4更新为3.
        else if(MaxV[j] < pArray[i] && pArray[i] < MaxV[j + 1])
        {
            MaxV[j + 1] = pArray[i];
        }
    }
    delete []MaxV;
    delete []LIS;
    return nMaxLis;
}

//获取数组元素的最小值
int getMinOfArray(int *pArray, int len)
{
    int result = pArray[0];

    for (int i = 0; i < len; i++)
    {
        if (pArray[i] < result)
        {
            result = pArray[i];
        }
    }
    return result;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值