题目:给定一个序列X[0···n],找出它的最长的单调递增子序列(Longest Increasing Subsequence)
分析:
思路一:
将序列X按非递减顺序排列,形成新序列Y,问题就转变成求解X和Y的LCS。
假设使用快速排序,则排序过程的时间复杂度为O(nlgn),而求两个序列的LCS的时间复杂度为O(n2)(因为X、Y长度相等),综合可知该解法的时间复杂度为O(n2)。
思路二:
对于X[0···n]中的每一个元素xi,求出以它结尾的X[0···i]的LIS,保存在数组lis中,然后找出lis中最大的元素,即X[0···n]的LIS。
假设求X[0···i]的LIS,即求lis[i],那么在X[0···i-1]寻找x0,x1,···,xi-1中所有小于xi的元素xj(0 <= j <= i-1 ),对于每一个xj,都有一个以它结尾的序列X[0···j]的最长单调递增子序列,其长度自然为lis[j],然后从这些lis[j]找出最大的元素,那么lis[i]就等于这个lis[j]加1,这就是X[0···i]的LIS。而如果X[0···i-1]中没有小于xi的元素,那么lis[i]等于1.
由此可以得到递归方程:
0 i == 0
Lis[i] =
max( lis[j] + 1, 1 ) X[j] < x[i] && j < i
该解法的时间复杂度为O(n2)。
思路三:
假设 result数组保存以X[i]结尾的最长递增子序列的长度,X的LIS的长度为K。由于长度为i( 1 <= i <= k )的LIS可能不止一个,那么我们用数组minInMax记录下长度相同的LIS的末尾元素中的最小值。
如果X[i] 大于 minInMax[ result[i-1] ] ,那么result[i] = result[i-1] +1,否则,result[i]与result[i-1]相等。由于minInMax是递增的,所以使用二分查找确定array[i]应该放在哪个位置上。
与思路二相比,思路三使用二分查找代替了顺序查找,使时间复杂度由o(n2)提高到了o(nlgn)。
思路二算法:
int lis( vector<int> array, vector<int> result )
{
int i, j;
for( i = 0; i < LENGTH; ++i )
{
result[i] = 1;
for( j =0; j < i; ++j )
{
if( array[j] < array[i] && result[j] + 1 > result[i] )
{
result[i] = result[j] + 1;
}
}
}
return maximum( result );//result存储以array[i]为末尾元素的LIS的长度,所以result数组的最大值即array的LIS的长度
}
思路三算法:
//result存储最长单调递增子序列的长度,minInMax存储长度为i的最长递增子序列中的末尾元素中的最小值
void lis2( vector<int> &array, vector<int> &result )
{
vector<int> minInMax( array.size() );
int i,low,high,mid;
result[0] = 1;
minInMax[ result[0] ] = array[0];
for( i = 1; i < array.size(); ++i )
{
if( array[ i ] > minInMax[ result[i-1] ] )//当前元素值大于array[0···i-1]序列中最长递增子序列中的最大值的最小值
{
result[i] = result[i-1] + 1;
minInMax[ result[i] ] = array[i];
}else
{
result[i] = result[i-1];
low = 0;
high = result[i-1];
mid = (low + high)/2;
while( low <= high )//由于minInMax是递增的,所以使用二分查找确定array[i]应该放在哪个位置上
{
if( array[i] > minInMax[mid] )
{
low = mid + 1;
}else{
high = mid - 1;
}
mid = (low+high)/2;
}
minInMax[low+1] = array[i];
}
}
}
程序文件,点击这里