问题描述
给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱)。例如:给定一个长度为6的数组A{5, 6, 7, 1, 2, 8},则其最长的单调递增子序列为{5,6,7,8},长度为4.
参考:https://blog.youkuaiyun.com/u013178472/article/details/54926531
解法1 最长公共子序列法 O(n^2)
这个问题可以转换为最长公共子序列问题。如例子中的数组A{5,6, 7, 1, 2, 8},则我们排序该数组得到数组A‘{1, 2, 5, 6, 7, 8},然后找出数组A和A’的最长公共子序列即可。显然这里最长公共子序列为{5, 6, 7, 8},也就是原数组A最长递增子序列。
解法2 动态归纳法(暴力法) O(n^2)
设长度为N的数组为{a0,a1, a2, ...an-1),则假定以aj结尾的数组序列的最长递增子序列长度为L(j),则L(j)={ max(L(i))+1, i<j且a[i]<a[j] }。也就是说,我们需要遍历在j之前的所有位置i(从0到j-1),找出满足条件a[i]<a[j]的L(i),求出max(L(i))+1即为L(j)的值。最后,我们遍历所有的L(j)(从0到N-1),找出最大值即为最大递增子序列。时间复杂度为O(N^2)。
解法3 O(nlgn)
首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1
然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1
接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2
以此类推
public int lis(int[] array){
if(null == array || array.length == 0){
return 0;
}
int len = 1;
int[] len2min = new int[array.length + 1];
len2min[0] = array[0];
for(int i = 1; i < array.length; i++){
if(array[i] > len2min[len - 1]){
len2min[len] = array[i];
len++;
}else{
//二分查找
int index = binSearch(len2min, array[i], len);
len2min[index] = array[i];
}
}
return len;
}
public int binSearch(int[] len2min, int arrayi, int len){
int left = 0;
int right = len - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(arrayi > len2min[mid]){
left++;
}else if(arrayi < len2min[mid]){
right--;
}else{
return mid;
}
}
return left;
}