最大上升子序列LIS(Java)

原文:http://www.cnblogs.com/Qmelbourne/p/5885648.html

有字符串str=”512487561”;先转化成char[] ch=str.toCharArray();定义int[] length=new int[ch.length];
思路是:以ch[i]结尾的最大上升子序列的长度存放在length[i]中。可以想象:

        for (int i = 1; i < ch.length; i++) {
            for (int j = 0; j <i; j++) {
                if (ch[i] > ch[j]) {}

也就是在”i”之前的字符中(也就是“j”代表的范围),如果有ch[i]>ch[j],那么这个字符就有可能是以ch[i]结尾的最大上升子序列的前一个字符(那么这个所谓的“前一个字符”是不是也和ch[i]一样,有它自己的“前一个字符”,直到前面没有能能接在前的字符为止),我们从这些有可能的字符中选择对应长度最长的(去length数组的对应位置找到最大值),然后再把这个最大值加1,再存放到length[i]上,一直到最后,然后取length数组中最大的数,也就是最大上升子序列的长度了。

原博文中的队列操的问题处理也很巧妙。博主没有仔细介绍思路,我在补充一下:先从左把遍历,得到一个length1数组,里面的值代表的是以ch数组当前位置为结尾(从左往右)的最大子序列的长度。然后将原数组逆序再重复一次,得到length2数组,然后再把length2逆序(逆序了才是一一对应),得到的就是以ch数组当前位置为结尾(从右往左)的最大子序列的长度。length1+length2,得到的就是以ch[i]结尾的左边最大上升子序列和右边最大上升子序列的长度之和,就是从两边向中间靠拢的感觉。当然要减去一个1,因为ch[i]在两个长度里都算了一次,重复了。此时求得的和的最大值也就是“山顶”了。也就是得到了一个分别从左从右看加起来最长的一个波浪形链。此时用ch.length减去这个最大值得到的就是要从中去除的那些同学数量。

我自己又思考了一下,把原来的方法改进了:

public class Lis {

    public static void main(String[] args) {
        String str="3223456189";
        System.out.println(lis(str));
    }

    public static int lis(String str) {
        char[] ch = str.toCharArray();
        int max;//记录当前对应的最大长度
        int ans = 0;//记录总共的最大长度
        for (int i = 1; i < ch.length; i++) {
            max = 0;//对应该ch[i]开始时,总是要初始化max
            for (int j = ans; j >= 0; j--) {
                if (ch[i] > ch[j]) {//找到比ch[i]小的
                    max = j + 1;//j代表的是ch[j]对应最大上升子序列的长度,加1也就是在该子序列后面再加ch[i]
                    ans=ans>max?ans:max;//如果ch[i]对应的长度大于ans,就赋值给ans
                    break;
                }
            }
            if( max==ans || ch[i]<ch[max])//max==ans表示当前得到的是最长的子序列,直接存下来就好了。否则得到的不是最长的,就要和之前的比较,存较小的。
                ch[max]=ch[i];
        }
        return ans+1;//下标加1才是长度。
    }
}

思路来源这篇博文:http://blog.csdn.net/qq_30241305/article/details/52068640(其实我只看懂了一点点,从中得到了一条信息:用数组下标值表示以当前字符结尾的最大上升子序列的长度)

举例说明思路:str=“21548974”
第一步:ch[0]=2,前面没有了,直接存在ch[0]中,此时ans(表示目前得到的最优解)=0,实际长度是ans+1.ch[0]就一个数,以他结尾的当然长度是1把。
第二步:ch[1] = 1 < ch[ans],所以以他结尾的长度也是0,但是,此时也就是说有两个长度为1的了,为了后面考虑,我们选择保留较小的一个(小的更容易被后面的数字接上),也就是ch[ans]=ch[1]。ans=0;
第三步:ch[2]=5>ch[ans],ans=ans+1=1,ch[ans]=ch[2]。
第四步:ch[3]=4,ch[3]< ch[ans],但是ch[3]>ch[ans-1],所以ans=(ans-1)+1=1,因为之前ch[2]对应的ans也等于1,那么我们同样取最小的保留,也就是ch[ans]=ch[3]。
第五步:ch[4]=8>ch[ans],ans=ans+1=2,ch[ans]=ch[4]。
……..

也就是:找到前面比他小的值,然后改变ans和保存该值(此时还要比较大小关系,具体看代码)。

代码的空间复杂度为O(n)(目前我看到的最小的也是O(n)),时间复杂度我算不清,应该是O(nlogn)(2为底)左右,甚至更优。

其实我还有个问题没有解决,如果想要输出这个最大序列,那么该如果做为好?想构造单链表形式的,也就是用一个标记,没时间了,就先放放吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值