1. 问题
对于给定的序列,求该序列中最长上升子序列长度
如:
8 2 7 1 9 10 1 4 3 5
最终输出4(最长上升子序列为[2,7,9,10])
2. 分析
2.1 方法一
很容易想到的就是O(n^2)的算法:
f[i]标识前i个元素中的最长不下降子序列的长度,则
f[i] = max(f[j]) +1, 0<j<i && a[j]<a[i]
2.2 方法二
换个思路,用d[i]标识以长度为i,最长上升子序列中元素的最小值,拿上面的例子来说
8 2 7 1 9 10 1 4 3 5
8
d[1] = 8 (目前为止,长度为1,最长上升子序列最小值当然是8)
8 2
d[1] = 2 (因为2比8小,同样长度为1,最小值需要更新,因为2比8更优)
8 2 7
d[1,2] =2, 7 (我们发现d[1]<a[3] = 7,直接长度加1,并把7添加到d中)
8 2 7 1
d[1,2] = 1,7 (为啥1的位置比2靠后,也能更新2?因为对于长度为1的最长不下降子序列,1作为结尾是最小的)
8 2 7 1 9
d[1,2,3] = [1,7,9]
8 2 7 1 9 10
d[1,2,3,4] = [1,7,9,10]
8 2 7 1 9 10 1
d[1,2,3,4] = [1,7,9,10]
8 2 7 1 9 10 1 4
d[1,2,3,4] = [1,4,9,10]
8 2 7 1 9 10 1 4 3
d[1,2,3,4] = [1,3,9,10]
8 2 7 1 9 10 1 4 3 5
d[1,2,3,4] = [1,3,5,10]
(为啥5的位置比9靠后,也能更新9?因为对于长度为3的最长不下降子序列,5作为结尾是最小的)
我们发现d数组并不是保存最终的最长上升子序列。假如已经计算出两个状态i,j满足A[i]i且i>j)来说,i并不会比j差——如果j满足Aj<Ak的条件,i也满足。换句话说,如果我们只保留i,一定不会丢失最优解。因此,对于相同的d值,只需要保留A数组中最小的一个即可。
对于A数组中的所有元素 A[i]
1)若d[len] < a[i],则len = len + 1, d[len] = A[i]
2)否则在d[1...len]中找到最大的t, 满足d[t]<A[i],此时用A[i]更新d,即d[t] = A[i]
步骤1时间复杂度是O(n),步骤2,因为d是递增的,所以可以用二分查找,时间复杂度是O(nlogn),因此整个算法复杂度为O(nlogn)
3. 代码
1 |
|
输入如下:
1 | 1 |
输出结果为:
1 | 2 |
和我们分析结果是一致的。