题目:
给定一个整数数组,求其最长递增子序列的长度。如对于数组[1, 0, 2, 3, 1],则其最长递增子序列有两个,分别为[0, 2, 3]和[1, 2, 3],这样对于这个数组的最长递增子序列的长度就为3。
解析1:
对于这一题,可以利用动态规划的思想来解决。对于数组A[0,..,n],我们用一个数组Len[1,..,n],Len[i]表示子数组A[0,..,i]中,以A[i]为尾的严格递增子序列的长度。那么便可以写出如下的递归式:
Len[i] = max{Len[j]+1 | j < i,and A[j] < A[i]}
根据这个表达式便不难写出代码,代码如下:
#define N 100000
int buffer[N];
int Len[N+1];
int longestIncLen_1(int n)
{
if(n <= 0) return 0;
Len[0] = 1;
int max = 1;
for(int i = 1; i < n; i++)
{
Len[i] = 1;
for(int j = 0; j < i; j++)
{
if(buffer[j] < buffer[i] && Len[j] + 1 > Len[i]) Len[i] = Len[i]+1;
}
max = Len[i]>max?Len[i]:max;
}
return max;
}
下面看看这种方法的复杂度,首先空间复杂度是O(n),至于时间复杂度,由于Len中保存的信息是无序的,所以对于每个位置i的元素,都需要将其与其前面i-1个元素进行比较,因此,比较次数为n(n+1)/2,即时间复杂度为O(n^2).
解析2:
对于上面的解法,我们可以进行一下改进。这次Len[i]中不再保存子数组A[0,..,i]中以A[i]为尾的子序列的长度,而保存数组中,所有子序列长度为i的子序列中,最小的尾元素。这样说可能有些拗口,举个例子。假设数组为A = [1, 2, 0, 1],那么长都为2的子序列有两个,分别为[1, 2]和[0, 1],那么此时Len[2] = min(2, 1) = 1.这样在遍历到A[i]时,则可以利用二分查找,找出那个max{j | Len[j] <= A[i]}.
接下来看下这种算法的复杂度如何。在最坏的情况下,空间复杂度为O(n),时间复杂度则为O(nlgn),这种情况当数组为严格递增有序的情况下出现。在最好的情况下,空间和时间复杂度均为O(1),这种情况即是数组为非递增有序。
根据这种思想,不难写出代码,如下:
#define N 100000
int buffer[N];
int Len[N+1];
int longestIncLen_2(int n)
{
int max = 1;
Len[1] = buffer[0];
for(int i = 1; i < n; i++)
{
int low = 1;
int high = max;
while(low < high)
{
int mid = low + ((high - low)>>1);
if(Len[mid] < buffer[i]) low = mid+1;
else high = mid -1;
}
if(Len[low] < buffer[i])
{
if(max < low + 1 || buffer[i] < Len[low+1])
Len[low+1] = buffer[i];
max = (low+1)>max?(low+1):max;
}
else Len[low] = buffer[i];
}
return max;
}