最长递增子序列
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]每一步分析如下:
i | array[i] | tail |
---|---|---|
0 | 2 | 2(初始值) |
1 | 1 | 1(替换2) |
2 | 4 | 1 4 |
3 | 3 | 1 3(替换4) |
4 | 1 | 1 3 |
5 | 5 | 1 3 5 |
6 | 6 | 1 3 5 6 |
代码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)); } }
相关文章推荐
- 最长递增子序列 (Longest Increasing Subsequence, LIS),POJ 2533, POJ 1631
- 最长递增子序列(LIS)-总结与相关展开
- 最长递增子序列(LIS)问题
- 用O(nlog(n)实现最长递增子序列问题
- 最长递增子序列问题—LIS
- 最长递增子序列(一维、二维)
- 最长递增子序列的数量
- 最长递增子序列O(NlogN)算法
- 生成最长递增子序列
- Java-LIS最长递增子序列(动态规划实现)
- 最长递增子序列
- 最长递增子序列问题
- 最长递增子序列 & 合唱队形 & 山峰数列
- 最长递增子序列
- 关于最长递增子序列的3种解法
- 最长递增子序列(编程之美)
- 使用动态规划求解最长递增子序列(LIS)
- 最长递增子序列(LIS)
- 最长公共子序列(动态规划方法)和最长递增子序列(动态规划方法)
- 最长递增子序列 O(NlogN)算法