例如在序列1,-1,2,-3,4,-5,6,-7中,其最长的递增子序列为1,2,4,6。
解法一:假设在目标数组array[]的前i个元素中,最长递增子序列的长度为LIS[i]。那么,LIS[i+1]=max{1,LIS[k]+1},array[i+1]>array[k],forany k<=i。(LIS[i]是指:假设在数组的前i个元素中,以array[i]为最大元素的最长递增子序列的长度)。
思想:穷举的方法,LIS中记录了长度,通过无后效性性质,只需与末尾的array[i]比较即可(很好理解),复杂度为O(N*N+N)=O(N)。
int LIS(int[] array){
int[] LIS =new int[array.length];
for(int i =0;i<array.length;i++){
LIS[i] =1;
for(int j= 0;j<i;j++){
if(array[i]>array[j]&&LIS[j]+1>LIS[i]){//穷举比较,获得最长长度
LIS[i]= LIS[j]+1;}
}
}
returnMax(LIS);
}
解法二:在递增序列中,如果i<j,那么就会有MaxV[i]<MaxV[j],并且不可能出现MaxV[i]>MaxV[j]这情况。
maxV[i]表示长度为i的递增子序列的最大元素的最小值;nMaxLIS表示数组最长递增子序列长度。
代码:
int LIS(int[] array){
int[] maxV =new int[array.length+1];//记录数组中的递增序列信息
int[] LIS =new int[array.length];
maxV[1] =array[0];
maxV[0] =min(array)-1;//数组中最小值-1,边界值。
for(int i =0;i<LIS.length;i++){
LIS[i] =1;
}
int nMaxLIS =1;
for(int i = 1;i<array.length;i++){
int j;
//先考虑当前最大长度子序列,看可否增加,不行再长度递减观察
for(j =nMaxLIS;j>=0;j--){
if(array[i]>maxV[j]){
LIS[i]= j+1;
break;
}
}
//如果当前最长序列大于最长递增序列长度,更新最长信息
if(LIS[i]>nMaxLIS){
nMaxLIS= LIS[i];
maxV[LIS[i]]= array[i];
}
//如果array[i]比最长递增子序列最大值要小,则替换原先的最大值(前面break处就有array[i]>maxV[j],添maxV[j]<array[i]感觉没啥必要···,有必要,因为上面运行下来maxV[j]不一定小于array[i])
elseif(maxV[j]<array[i]&&array[i]<maxV[j+1]){
maxV[j+1]= array[i];
}
}
returnnMaxLIS;
}
时间复杂度为O(N*N)。内部循环穷举查询那边,可以用二分搜索加速,把时间复杂度降为O(N*log2N)。
附加:最长公共上升子序列
设f[j]为必选择b[j]为末尾时的最长公共上升子序列
代码如下:
//通过固定a[i],循环b[j]进行比较。很好理解,忘记了,可以结合1,2,0,4,5和1,0,4,5,2的例子跟代码一起理解。
int i,j,k;
for(i=0;i<n;i++){
k = 0;
for(j =0;j<m;j++){
if(a[i]== b[j])
if(f[j]<f[k]+1)
f[j]= f[k]+1;
//必须要有,很好理解当a[i]值比b[j]大时,需要更新k,因为能够去与f[j]比较了,而a[i]比b[j]小时,是没资格去获取f[j]
if(a[i]>b[j])
if(f[k]<f[j])
k= j;
}
}
返回max(f(i));