最长递增子序列描述的是这样一个问题——给定一个混乱序列a1,a2,...an,要求找到他的一个子序列ai1,ai2,...aik,这k个数的下标严格增大,且k值达到最大。这道题根据需要的不同有不同的做法,首先来看一个比较简单的需要——求这个LIS的长度。
求长度可以直接用栈的方法,初始时len = 1,从首元素开始压栈,第i个元素如果大于栈顶元素就压栈且len++,如果第i个元素小于栈顶元素,则查找栈中比该元素小的第一个元素并替换,当遍历完整个数组后,len的值就是LIS的长度,但是栈中的内容并不是LIS,这也是这种方法的局限性。
那么怎样才能做到既输出长度又输出整个LIS呢?
首先要知道栈的方法由于有替换的过程,但替换来的不一定能构成LIS,但是可以作为一个新的LIS的“备选”,当备选的长度超过现有的长度时,我们就可以用备选者替换当前的LIS。
可以发现,每次添加一个元素,要么作为最小的元素新开启一个LIS,要么作为递增的大元素加在LIS的后面,当它成为中间元素时,我们则需要找到一个长度s,使这个元素恰好在LIS(s)和LIS(s-1)的末尾元素之间,那么他就可以作为LIS(s)的新末尾元素。
而说到这里,其实这就是一个关于“位置”的设计,相同位置的取舍就在于哪一个元素所在的LIS更长。代码如下,pos数组记录的就是每个元素的的前一个元素的位置,而MTS(i)表示长度为i的LIS的末尾元素的位置。代码最后还对pos打了表,方便看出它的作用。
#include <iostream>
using namespace std;
int main()
{
int a[10] = { 0,1,2,3,8,9,4,5,6,10 }, MTS[10] = { 0 }, pos[10] = { 0 };
int len = 1;
MTS[1] = 1;
pos[1] = 0;
for (int i = 2; i < 10; i++)
{
if (a[MTS[1]] > a[i])
{
pos[i] = 0;
MTS[1] = i;
}
else if (a[MTS[len]] <= a[i])
{
pos[i] = MTS[len];
len++;
MTS[len] = i;
}
else
{
int mid, up, low;
low =1;
up =len;
while (low <= up)
{
mid = (low + up) / 2;
if (a[MTS[mid]] > a[i])
up = mid-1 ;
else
low = mid+1;
}
pos[i] = MTS[mid];
MTS[mid + 1] = i;
}
}
int pt = MTS[len];
for (int i = 1; i <= len; i++)
{
cout << a[pt]<<",";
pt = pos[pt];
}
cout << endl;
cout << len << endl;
for (int i = 1; i < 10; i++)
cout << pos[i] << ",";
cout << endl;
for (int i = 1; i < 10; i++)
cout << a[i] << ",";
return 0;
}