最长递增子序列(Longest Increasing Subsequence)

定义

最长上升子序列(Longest Increasing Subsequence,LIS),在计算机科学上是指一个序列中最长的单调递增的子序列。

问题描述

给定一个长度为 N 的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)。例如:给定一个长度为 5 的数组{5, 6, 1, 2, 8},则其最长的单调递增子序列为 {5,6,8},长度为 3。

解法

动态规划

时间复杂度

该方法的时间复杂度为 O(n^{2})

实现过程

下面我们用一个实例来分析一下动态规划求解 LIS 的整个过程。假设数组 A 的内容为 {5, 6, 1, 2, 8}。

1、第一个元素直接设置 LIS 长度为 1 即可。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第1张图片

2、第二个元素 6 大于前面所有元素进行比较。5<6,则 LIS[1] = LIS[0]+1 = 2。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第2张图片

3、第三个元素 1 和前面的所有元素进行比较。1<6,则 LIS 的长度可能为 1;1<5,则 LIS 的长度可能为 1;取最大值,LIS[2]=1 。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第3张图片

4、第四个元素 2 和前面的所有元素进行比较。1<2,则 LIS 的长度可能为 LIS[2]+1 = 2;6>2,则 LIS 的长度可能为 1;5>2,则 LIS 的长度可能为 1;取最大值,LIS[3]=2 。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第4张图片

5、第五个元素 8 和前面的所有元素进行比较。2<8,则 LIS 的长度可能为 LIS[3]+1 = 3;8>1,则 LIS 的长度可能为 LIS[2]+1 = 2;8>6,则 LIS 的长度可能为 LIS[1]+1 = 3;8>5,则 LIS 的长度可能为 LIS[0]+1 = 2;取最大值,LIS[4]=3 。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第5张图片

算法思路

设长度为 N 的数组为 {a0,a1, a2, ..., an-1),则假定以 aj 结尾的数组序列的最长递增子序列长度为 LIS(j),则 LIS(j) = {max(LIS(i))+1, i

二分查找

时间复杂度

该方法的时间复杂度为 O(n*logn)

算法描述

我们可以引入一个新数组 maxV,该数组的特性为:

长度为 1 的递增子序列最大元素的最小值为 maxV[1];

长度为 2 的递增子序列最大元素的最小值为 maxV[2];

长度为 LIS[i] 的递增子序列最大元素的最小值为 maxV[LIS[i]]。

首先,证明 maxV[] 是递增的,因此可以使用二分搜索。我们可以用数学归纳法即可证明:若前 k 个元素是递增的,一定有 maxV[k+1] \geq maxV[k]

证明:假设不成立,则 maxV[k+1] < maxV[k]。

根据 maxV[k+1] 的定义,存在一个长度是 k+1 的 LIS,并且以 maxV[k+1] 为最大元素。将上述子序列去掉最后一个元素maxV[k+1], 得到长度为 k,且最大元素 < maxV[k+1] < maxV[k]。

这显然与 maxV[k] 的定义矛盾。所以假设不成立。

实现过程

我们用一个实例来分析一下二分查找求解 LIS 的整个过程。假设数组 A 的内容为 {5, 6, 1, 2, 8}。

我们用 LIS[i-1] 表示长度为 i 的最长递增子序列末尾的数据。

1、第一个元素直接加入到 LIS 数组中。LIS[0]=5,表示长度为 1 的 LIS 数组最后一个元素是 5。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第6张图片

2、第二个元素为 6,因为 6>LIS[0],构成递增,将数字 6 加入到 LIS 数组中,即 LIS[1]=6,表示长度为 2 的 LIS 数组的末尾是 6。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第7张图片

3、第三个元素为 1,1

最长递增子序列(Longest Increasing Subsequence)_第8张图片

4、第四个元素为 2,2

最长递增子序列(Longest Increasing Subsequence)_第9张图片

4、第五个元素为 8,8>LIS[2],构成递增,将数字 8 加入到 LIS 数组中,即 LIS[2]=8,表示长度为 3 的 LIS 数组的末尾是 8。如下图所示。

最长递增子序列(Longest Increasing Subsequence)_第10张图片

这样,我们完成了遍历,这时候我们可以发现 LIS 数组的小标为 2,表示我们要求解的 LIS 长度为 3。

算法思路

将 array[i] 在当前的 maxV[] 数组中进行二分搜索,找到位置 k,maxV[k] < array[i] < maxV[k+1]。

将 array[i] 加入 maxV[] 数组。仅仅影响 maxV[k+1]  (maxV[k+1] = array[i]),而对其他的元素不产生影响。

参考实现

int LIS(int *a, int n) {
    if (n<=0) {
        return 0;
    }

    vector maxV;
    maxV.push_back(a[0]);
    for(int i=1; i *maxV.rbegin()) {
            maxV.push_back(a[i]);
        } else {
            *lower_bound(maxV.begin(), maxV.end(), a[i]) = a[i];
        } 
    }
    return maxV.size();
}

 

你可能感兴趣的:(OI,#,动态规划,#,查找,最长递增子序列,LIS)