最长递增子序列长度LIS

最长递增子序列Longest Increasing Subsequence (LIS) 指一个序列中所包含的最长的递增子序列。
 例如, 序列{2, 1, 4, 2, 3, 7, 4, 6}的LIS是{1, 2, 3, 4, 6},其长度为5。写一函数计算一个序列中的最长子序列长度。

 

一般思路:使用动态规划,数组记为a[],用f[i]表示从第0个元素到第i个元素的LIS长度(初始化为1,因为a[i]本身长度为1),对于每一个i,需要和i以前的元素进行比较,并更新f[i]的值,即比较a[i]和a[j],0 <= j < i。

状态转移方程为f[i] = max{f[i], f[j]+1, if a[i] > a[j]}, 0 <= j < i。 用result记录最长的LIS长度。时间复杂度为O(N^2)。

code:

View Code
 1 //最长递增子序列长度Version1, DP
 2 int LongestIncreasingSubsequenceV1(int *a, int N)
 3 {
 4     if (NULL == a || N <=0)
 5     {
 6         return -1;
 7     }
 8     int *f = new int[N];
 9     //初始化f[i]
10     for (int i = 0; i < N; i++)
11     {
12         f[i] = 1;
13     }
14     
15     int result = f[0];
16 
17     for (int i = 1; i < N; i++)
18     {
19         for (int j = 0; j < i; j++)
20         {
21             if (a[i] > a[j] && f[j] + 1 > f[i])
22             {
23                 f[i] = f[j] + 1;
24             }
25         }
26         if (result < f[i])
27         {
28             result = f[i];
29         }
30     }
31     delete f;
32     f = NULL;
33     return result;
34 }

 

改进:上面比较a[i]和a[j]是通过遍历来比较的,有没更好的方法?

假如使用B[len]来记录长度为len的LIS的最小末尾,那么就可以直接比较a[i]和B[len],找到a[i]在B中合适的位置。

如序列a[8]={2, 1, 4, 2, 3, 7, 4, 6},初始B[0] = (signed int)0x80000000,B[1] = a[0] =2;

对于a[1],a[1]<B[1],更新B[1] = 1;

对于a[2],a[2]>B[1],故B[2] = 4;

对于a[3],B[1]<a[3]<B[2],更新B[2] = 2;

对于a[4] ,a[4]>B[2],故B[3] = 3;

对于a[5] ,a[5]>B[3],故B[4] = 7;

对于a[6],B[3]<a[6]<B[4],故更新B[4]=4;

对于a[7],a[7]>B[4],故B[5]=6.至此过程结束。

 

从上面过程可以发现,对于长度为i的最小末尾数组B[i],有以下性质,当i<j时, B[i]<B[j]。在有序数组中查找,可以使用二分查找提高查找效率。

设当前总长度为k时,比较a[i]和B[k]

1.若a[i]>B[k],则B[k+1] = a[i]

2.若a[i]<B[k], 二分查找B中位置j使得,B[j]<a[i]<B[j+1],并更新B[j+1] = a[i];

这两种情况可以合并在一起。

code:

View Code
 1 //最长递增子序列长度Version2, 改进的DP
 2 int LongestIncreasingSubsequenceV2(int *a, int N)
 3 {
 4     if (NULL == a || N <=0)
 5     {
 6         return -1;
 7     }
 8     
 9     int *B = new int[N + 1];
10     B[0] = (signed int)0x80000000;
11     B[1] = a[0];
12     int len = 1;
13     int low;
14     int high;
15     int mid;
16     for (int i = 1; i < N; i++)
17     {
18         low = 0;
19         high = len;
20         while (low <= high)
21         {
22             mid = low + (high - low) / 2;
23             if (a[i] > B[mid])
24             {
25                 low = mid + 1;
26             }
27             else
28             {
29                 high = mid - 1;
30             }
31         }
32         B[low] = a[i];
33         if (low > len)
34         {
35             len++;
36         }
37     }
38     delete B;
39     B = NULL;
40     return len;
41 }

 

posted @ 2013-03-29 13:47  hust_枫  阅读(261)  评论(0编辑  收藏  举报