求数组的最长严格递增子序列

题目:

给定一个整数数组,求其最长递增子序列的长度。如对于数组[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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值