动态规划(篇2)最长递增子序列(LIS)

47 篇文章 37 订阅
9 篇文章 2 订阅

【题目】

给定数组arr,返回arr的最长递增子序列。

【例子】
arr=[10,22,9,33,21,50,41,60,80],返回的最长递增子序列为[10,22,33,41,60,80]长度为6。

解法 1

思路:

1.定义长度为n的dp数组,dp[i]表示为arr[i]结尾的最长递增子序列的长度。

2.对于第一个数arr[0]来说dp[0]=1,依次求出以i结尾的最长递增子序列

3.对于dp[i],求arr[i]结尾的最长递增子序列,在arr[0..i-1]中选出比arr[i]小且长度最长的

dp[j] , dp[i] = Math.max( dp[0…i-1] , dp[i] );如果所有的 dp[0…i-1] 都比 dp[i] 大,则 dp[i]=1;

代码:

//最优解:
public static int LongestSubString(int arr[])
    {
        int len=0;
        if(arr==null||arr.length==0)
            return 0;
        int dp[]=new int[arr.length];
        dp[0]=1;
        //dp[i] 表示到i为止是最长递增子序列的长度
        for(int i=1;i<arr.length;i++)
        {
            dp[i]=1;
            for(int j=0;j<i;j++)
            {
                if(arr[i]>arr[j])
                {
                //求dp[i]时遍历,dp[0...i-1],找出arr[j]<arr[i]小且dp[j]是最大的
                //dp[i]=dp[j]+1;
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
        }   
        for(int i=0;i<arr.length;i++)
        {
            len=Math.max(dp[i],len);
        }
     return len;

    //最优值,求出dp[i]之后要求出递增子序列[10,22,33,41,60,80]
    //先找到最大dp[i],从后往前,如果dp[i]==dp[j]+1,且arr[i]>arr[j],则可知arr[j]是子序列中
    //arr[i]前面的数.

    public  generateLIS(int arr[],int dp[])
    {
        int k=0;
        int index=0;
        int len=0;
        for(int i=0;i<arr.length;i++)
        {
            if(dp[i]>len)
            {
                len=dp[i];
                index=i;
                //找到递增子序列中的最后一个元素[10,22,33,41,60,80]中的80,
            }
        }
        int subArr[]=new int[len];
        subArr[k++]=arr[index]; 
        for(int j=index-1;j>=0;j--)
        {
            if((dp[index]==dp[j]+1)&&(arr[index]>arr[j]))
            {
                //从后向前,将属于递增子序列的元素加入到subArr中。
                subArr[k++]=arr[j];
                index=j;
            }
        }
        for(int j=subArr.length-1;j>=0;j--)
        {
            System.out.print(subArr[j]+" ");
        }

    }

解法2

思路
生成 dp 数组时使用二分查找算法来进行。

用ends数组来保存单调序列,这个序列并不是 最优值的单调序列,对于arr[i],如果比 ends 中的每个

数都大时放到 ends 后面,否则用arr[i]代替ends数组中第一个比arr[i]大的数,保持 ends 递增。这样

每一个arr[i]在ends的位置就是 dp[i] 的值。

public static int LongestSubString1(int arr[])
    {
        if(arr==null||arr.length==0)
            return 0;
        int l=0,r=0,right=0;
        int len=0;
        int ends[]=new int[arr.length];
        int dp[]=new int[arr.length];
        dp[0]=1;
        ends[0]=arr[0];
        //ends[0]开始时存放arr[0],
        for(int i=1;i<arr.length;i++)
        {
            l=0;
            r=right;
            //如果arr[i]比 ends 数组中的所有元素都大则放到 ends 后面,
            //否则用arr[i]代替ends数组中第一个比arr[i]大的数,保持 ends 递增。
            while(l<=r)
            {
                int mid=(l+r)/2;
                if(arr[i]>ends[mid])
                {
                    l=mid+1;
                }
                else {
                    r=mid-1;
                }
            }
            right=Math.max(l,right);
            ends[l]=arr[i];
            dp[i]=l+1;
            //arr[i]在数组 ends 中的位置,就是以arr[i]结尾的最长单调子序列的长度。
        }       
        len=right+1;
        generateLIS(arr,dp);
        return len;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值