简介: 最长递增子序列(longest increasing subsequence)LIS问题是指,在一个给定的数值序列中,找到一个子序列(元素在原序列中不一定是连续的,子串是连续的),使得这个子序列元素的数值依次递增,并且这个子序列的长度尽可能大。
时间复杂度O(n^2)的解法
算法思想: 假设要计算序列a[0]…a[n-1],a[n]的LIS,引入辅助数组dp,dp[i]存储以a[i]结尾的LIS。dp[0]=1,即以a[0]结尾的(只有1个数)LIS为1,最优子结构为:dp[i] = max(dp[j]) +1 (a[j]<a[i] && 0<=j<i )
public static int LIS(int[] a){
int n;
if (a == null || (n = a.length) == 0)
return 0;
int maxlen = 1,clen;
int[] dp = new int[n];
dp[0] = 1;
for (int i = 1;i < n;i++){
clen = 0;
for (int j = 0;j < i;j++)
if (a[j]<a[i] && clen<dp[j])
clen = dp[j];
dp[i] = clen + 1;
if (maxlen < dp[i])
maxlen = dp[i];
}
return maxlen;
}
时间复杂度O(nlogn)的解法
算法思想: 在查找dp[i]前面满足a[j]<a[i]的最大dp[j]时,由顺序查找改为二分查找,时间复杂度即可降为O(nlogn)。
另外引入一个数组b,b中的元素满足b[ dp[i] ] = a[i],即当最长递增子序列的长度为dp[i]时子序列的末尾元素为b[ dp[i] ] = a[i](若有多条序列长度相同,取末尾元素最小的),此时数组c的下标表示长度,数组b的值表示该长度(下标)下的递增子序列的末尾元素。可证明数组b是非递减的。
public static int LIS(int[] a){
int n;
if (a == null || (n = a.length) == 0)
return 0;
int maxlen = 1,index;
int[] b = new int[n+1];
b[1] = a[0];
for (int i = 1;i < n;i++){
if (a[i] > b[maxlen]){
maxlen++;
b[maxlen] = a[i];
}else {
//在数组b里找到大于等于a[i]的下标
index = binarySearch(1, maxlen, b, a[i]);
b[index] = a[i];
}
}
return maxlen;
}
public static int binarySearch(int start, int end, int[] b, int target) {
int pivot;
while (start <=end) {
pivot = (start + end) / 2;
if (target >= b[pivot])
start = pivot + 1;
else
end = pivot - 1;
}
return start;
}