最长不降子序列

比如arr={1,5,8,2,3,4}的最长不降子序列是1,2,3,4

  • 动态规划的思路:用P[i]去记录以i结尾的最长的不降子序列的长度,比如arr中,P[0]=1;P[1]=2;P[2]=3;P[3]=2;P[4]=3;P[5]=4;

  • 那P是如何生成的:首先P数组初始化每个值都为1,因为每个数自身也算做长度为1的不降子序列;其次在算P[i+1]时要去找从0-i中哪些数比a[i+1]小,从这些元素的P中找到一个最大的P[j],则P[i+1]=P[j]+1就是当前以i+1结尾的最大的不降子序列长度了;最终在P中找到最大的值就是当前数组的最大的不降子序列的长度,同时可以输出这个子序列。

  • 代码如下:

int longest_increasing_sequence1(int a[], int n)
//最长不降子序列 O(N^2),能输出子序列的值
{
    int D[10] = { 1,1,1,1,1,1,1,1,1,1 };
    //记录到第i个数位置,最长子序列的长度
    for (int i = 1; i < n; i++)
    {
        for (int j = 0; j < i; j++)
        {
            if (a[j] <= a[i])//从头找比当前数小的数进行比较
            //<=考虑到了相同的数字也组成了不降序列
            {
                D[i] = ((D[j] + 1)>D[i]) ? (D[j] + 1) : D[i];
                //保证能得到当前最长的子序列
            }
        }
    }
    int max = 1;
    for (int i = 0; i < 10; i++)
        max = ((D[i]>max) ? D[i] : max);
    return max;
}
  • 另一种方法也是从网上得知的,比较巧妙,复杂度为O(nlogn),但是无法输出具体的子序列。思路如下:
    – 用数组D[size]去存长度为size的不降子序列,最后一位数最小的值是D[size];例如a[i]元素:

        if a[i]>=D[size]:size++;D[size]=a[i];
        else:find(D[j-1]<=a[i]<D[j]);D[j]=a[i];
    

    – 也就是说,如果要比较的数比当前最长不降子序列最后一位数的最小值要大,说明这个数可以被加入到这个子序列中去,那么直接将子序列的长度size+1且更新size+1时子序列最后一位的值;如果要比较的数小于子序列最后一位数的最小值,说明这个数只能去找较小的size的子序列,然后插入到这个较小的子序列中去,寻找较小的size=j的方法是二分查找的思想:从0-size中去二分,直到找到了这样一个j,使得D[j-1]<=a[i]< D[j],这样a[i]就可以放在D[j]上,更新D[j]=a[i],但此时size并不会更新;之所以等号放在D[j-1]这里是为了保证当a[i]与D[j-1]相等时,仍构成一个不降的序列。

  • 代码如下:

int b_search(int b[], int m, int k)//二分查找,找长度为m的b中,最小的大于l的数的坐标
//记住二分查找的两个写法:递归和非递归,递归情况形参有四个包括left和right指针,用if(l<=r)判断条件;非递归情况形参只有三个包括size即可,用while(l<=r)判断条件
{
    int l = 0;
    int r = m - 1;
    while (1 <= r)
    {
        int mid = (l + r) / 2;
        if ((b[mid - 1] <= k) && (b[mid] > k))
        //不能写成a<b<c的形式,这样即使出现了重复,也会将重复的数计入到总长度里
            return mid;
        else if (b[mid - 1]>k)
            r = mid - 1;
//即使l=b[mid],也可以让l=mid+1,因为最终还会比较l与l-1两者的范围
            l = mid + 1;
    }
    return 0;//如果比第一个数还要小,则返回-1
}

int longest_increasing_sequence2(int a[],int n)
//最长不降子序列O(nlogn) 无法输出子序列的值
{
    int D[10];
    D[0] = a[0];
    //D[[i]记录最长子序列的长度为i+1,且此序列最后一位最小的数为D[i]
    int size = 1;//记录最长子序列的长度为size=i+1
    for (int i = 1; i < n; i++)
    {
//如果比当前最大长度的最小值大,则长度加一
        {
            D[size] = a[i];
            size++;
        }
        if (a[i] < D[size - 1])
        //如果比当前最大长度的最小值小,去长度较小的里面找能否符合
        {
            int j = b_search(D, size, a[i]);
            D[j] = a[i];
        }
    }
    return size;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值