最长上升子序列(Longest Increasing Subsequence)

给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1<s2<s3<…<sn并且这个子序列的长度最长。输出这个最长的长度。(为了简化该类问题,我们将诸如最长下降子序列及最长不上升子序列等问题都看成同一个问题,其实仔细思考就会发现,这其实只是<符号定义上的问题,并不影响问题的实质)
例如有一个序列:1 7 3 5 9 4 8,它的最长上升子序列就是 1 3 4 8 长度为4.

分析:
对于几个给定的数字
a1、a2、... an, 
必然存在最长上升子路径。有了路径,那么对于路径上的非起始节点ai而言,肯定会存在一个节点是排在ai前面的,不妨记为prev(ai),叫做前驱节点
现在,可以给出问题中的最优子结构了:到达节点ai的最长递增路径,包含了到达这条路径上的每一个节点的最长递增路径。自然地,最长递增路径也就包含了到达前驱节点的最长递增路径。至于证明,可以用反证法简单地做到。
设L(ai)为到达节点ai的最长递增子序列的长度,并且它一般可以定义为
L(ai)=1+max{L(aj), aj<-prev(ai)}。 
用文字描述就是:到达节点ai的最长递增子序列的长度,比所有可能的路径上的到达前驱节点aj的子序列的长度中的最大值还要多一。

例子:

原始数据为  5 2 8 6 3 6 9 7 

那么赋予它们的每一个数的初始长度均为0,所以辅助数组的元素全为0并且长度为8。接下来从5出发,对于所有比5大的数,即可以作为5的后续节点,修改为1。那么经过第一轮修改后,辅助数组变成
0 0 1 1 0 1 1 1。 
而对于从2开始的情况,在修改时需要注意一点:当发现后续节点所对应的长度已经比先有长度加上一还要大时,就不应该修改了。因为根据最优解的值的递归定义,每一个节点对应的最长子序列的长度在构造时所选取的都是前驱节点中的长度的最大值。所以,第二轮变换后辅助数组变成了
0 0 1 1 1 1 1 1。 
变化不大,只是第五个数字3对应的长度变成了1而已。这是对的,因为在3之前,能够作为前驱节点的也只有2而已,所以它们之间的距离是1,加上原来2对应的长度0,还是1。剩下的步骤和之前进行的这两步是一致的,于是到最后,辅助数组就会变成
0 0 1 1 1 2 3 3。 
于是我们知道,在数字中拥有最大的长度的,也就是到达它们的递增子序列最长的数字是9和7。那么现在有了两个数组,一个是原来的输入,另一个则是计算出来的辅助数组,通过这两个数组,就可以得到在输入中所存在的至少一个最长递增子序列了。方法也很简单,首先需要找出在辅助数组中具有最大值的元素的下标是几。显然,下标是6和7(从0开始计算的下标),选择一个来演示就可以了,例如6。那么从下标为6的元素位置开始,可以知道它对应的子序列的长度为3,那么它的前驱对应的子序列的长度应该为2,于是将下标不停地减小,直到找到这样的一个数:这个数本身要比测试的数字小,并且它对应的长度恰好比测试的数字对应的长度小一。并且这样一直进行下去,直到找到了的数字对应的长度为0为止。此时把这个最后的数字加入已经得到的序列中,就可以得到在输入中存在的“最长递增子序列”了。

 

代码:

算法复杂度:O(n^2)

public class LIS {
    
    public static int getLIS(int arr[], int n) {
        int dp[] = new int[n];
        for (int i = 0; i < n; i++) {
            dp[i] = 0;
        }   
        int ans = 0;
        for (int i = 0; i < n-1; i++) {
            ans = dp[i];
            for (int j = i+1; j < n; j++) {
                if (arr[i] < arr[j] && dp[j] < ans+1) {
                    dp[j] = ans+1;
                }                    
            }
        }
        ans = 0;
        for (int i = 0; i < n; i++) {
            if(dp[i] > ans) {
                ans = dp[i];
            }
        }
        return ans;
    }
}

不过,这个方法只是得到最长子序列的长度,并没有实现得到最长子序列。

posted on 2013-12-18 13:24  SherryIsMe  阅读(753)  评论(0编辑  收藏  举报

导航