您的位置:首页 > 其它

最长递增子序列

2017-04-23 17:12 162 查看

题目

求一个数组中最长递增子序列,要求时间复杂度尽量低。

测试输入

7

2 1 4 3 1 5 6

测试输出

4

分析1

采取从后向前的分析思路。如果已知第i个元素存在于最长递增子序列中,那么前i个元素的最长递增子序列的问题可以转化为,在前i−1个元素中所构成的最长递增子序列,加上这个第i个元素。由此可见子问题具有独立性,可以使用动态规划来完成。数组array[i],i=0∼n−1,定义lis[i]表示以第i个元素结尾的最长递增子序列长度,则原问题为max(lis[i]),i=0∼n−1。要得到以array[i]结尾的最长递增子序列长度lis[i],就要在数组中找到array[k],k=0∼i−1,要求array[k]<array[i]且lis[k]最大。有一种情况是array[k]都比array[i]大,那么以array[i]结尾的序列长度就是其自身长度1。递归式为

lis[i]=⎧⎩⎨⎪⎪1,1,max(lis[k])+1,i=0array[k]≥array[i] for all k=0∼i−1if array[k]<array[i] for k=0∼i−1

代码1

import java.util.Scanner;

public class LIS {
static int solution(int[] array, int[] lis) {
int n = array.length;
lis[0] = 1;
for (int i = 1; i < n; i++) {
int maxLen = 0;// array[i]之前的最长子序列长度
for (int k = 0; k < i; k++) {
if (array[k] < array[i] && lis[k] > maxLen) {
maxLen = lis[k];
}
}
// 若没有找到比array[i]小的值,array[i]构成长度为1的子序列
// 若找到则在最长递增序列末尾加上array[i]构成新最长子序列
lis[i] = maxLen + 1;
}
return max(lis);
}

static int max(int[] array) {
int maxValue = Integer.MIN_VALUE;
for (int i : array) {
if (i > maxValue) maxValue = i;
}
return maxValue;
}

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] array = new int
;
for (int i = 0; i < n; i++) {
array[i] = sc.nextInt();
}
int[] lis = new int
;
System.out.println(solution(array, lis));
}
}


分析2

上述做法时间复杂度为O(n2),原因在于max查找需要遍历一遍数组。如果能将查找过程优化就能得到更优的解法。应该注意一个事实,在找前i个元素的最长递增子序列时,应该尽量保证已经找到的子序列的末尾元素尽量小,这样第i+1个元素才更容易构成新的子序列。由此可以换一种定义方式,tail[len]表示长度为len的最长递增子序列的末尾元素,如果一个新来的元素x大于tail[len],则这个新元素x可以成为新的最长递增子序列的末尾元素,len增加1,否则就要用它替换tail[k],k=0∼len−1满足tail[k]≥x且tail[k−1]<x,这意味着长度为k的最长递增子序列的末尾元素更新为x,len保持不变。用更小的x替换了tail[k],在不影响原有结果的情况下,更容易使新来的元素成为新子序列的末尾。另外,原问题中最长递增子序列长度就是len。对于数组
[2 1 4 3 1 5 6]
每一步分析如下:

iarray[i]tail
022(初始值)
111(替换2)
241 4
331 3(替换4)
411 3
551 3 5
661 3 5 6
可以看到数组tail就保存着最长递增子序列的所有值,且其中的每个元素总是在可选元素的最后一个。因为数组tail一定递增,如果将查找过程改为使用二分查找,则整个算法的时间复杂度为O(nlog(n))。

代码2

import java.util.Scanner;

public class LIS {
static int solution2(int[] array, int[] tail) {
int n = array.length;
tail[0] = array[0];
int maxLen = 1;
for (int i = 1; i < n; i++) {
if (array[i] > tail[maxLen - 1]) {
tail[maxLen] = array[i];
++maxLen;
} else {// 线性查找
for (int k = 0; k < maxLen; k++) {
if (tail[k] >= array[i]) {
tail[k] = array[i];
break;
}
}
}
}
return maxLen;
}

static int solution3(int[] array, int[] tail) {
int n = array.length;
tail[0] = array[0];
int maxLen = 1;
for (int i = 1; i < n; i++) {
if (array[i] > tail[maxLen - 1]) {
tail[maxLen] = array[i];
++maxLen;
} else {// 二分查找
int pos = binarySearch(tail, maxLen, array[i]);
tail[pos] = array[i];
}
}
return maxLen;
}

static int binarySearch(int[] array, int limit, int value) {
int start = 0;
int end = limit - 1;
while (start <= end) {
int mid = start + (end - start) / 2;
if (array[mid] == value) {
return mid;
} else if (array[mid] < value) {
start = mid + 1;
} else {
end = mid - 1;
}
}
return start;
}

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] array = new int
;
for (int i = 0; i < n; i++) {
array[i] = sc.nextInt();
}
int[] tail = new int
;
System.out.println(solution2(array, tail));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: