LIS:最长上升子序列(3种方法)

最长上升子序列是我在计蒜客中的蓝桥杯模拟题中遇到的。
当事看到题目自己想了想,和最长相同子序列差不多。然后自己写了两种解法
一种是采用递归式的方法(时间复杂度估计2^n),
另一种事采用了动态规划的方式(时间复杂度n*n)。
后来得知还存在一种时间复杂度更加优秀的二分法(时间复杂度n * logn), 于是看了下面的博客。

参考博客
这篇博客中二分法说的还算清楚(反正我一遍就了解得差不多 ),但还是有一些缺憾(为什么要用二分查找来经行优化没有说明)。
后来我自己研究了一小会有所明悟,故此总结

下面就直接进入二分法:
假设有一段序列A2 7 8 4 9 5 3
再构建一个数组B:用来查询最优结果。
对于LIS来,说它的结果序列必定是一个由小到大的递增序列。(最重要的是有序的)。
于是对与数组B来说那么它也必定是一个有序的。
我们不妨每次选取最大的放在后面, 小的放在前面来经行查询。
B[0]初始化为最小值。
然后经行3轮选取,后就有了下面的数组B:2 7 8

在后面继续经行选取时会遇到47是大于4的第一个数,于是可以将7直接替换成4
注意:这里的替换并不会影响最长上升子序列的长度(因为替换并没有改变序列的相对位置)。
接下来继续后面的选取:

看到这里是不是觉得so easy 啊!
是的,只需要扫描一遍数组A, 然后每次在B中找到需要替换, 或者增加的位置就行。
这里就需要二分查找法。找到在序列B中第一个大于或等于当前关注点的位置。如果末尾值小于当前关注点(那么就可以直接添加到末尾)。

最后B数组的长度就是LIS的结果了。

最后 show my code。

/*找到第一个比队列中大的位置*/
int binary_search_upper(int* A, int left, int right, int key){
	if(A[right] < key){
		return right;
	}
	if(A[right] == key){
		return right;
	}
	int mid;
	while(right > left){
		mid = left + (right - left) / 2;
		if(A[mid] <= key){
			left = mid + 1;
		}else{
			right = mid;
		}
	}
	return left;
} 
int LIS_nlogn(int* A, int n){
	
	int* B = new int[n + 1];
	int len = 0;
	int next;
	B[0] = M;
	for(int i = 0; i < n; i++){
		next = binary_search_upper(B, 0, len - 1, A[i]);//  找到在序列B中第一个大于或等于当前关注点的位置
		B[next] = A[i];
		if(next >= len - 1){// 记录长度, 以免后面还需要扫描一遍查询长度 
			len = next + 1; // 这里记录的时长度, next是下标,所以需要加减1
		}
	}
	delete []B;
	return len;
}

end

DP:

 // 最长上升序列
 // D 序列代表1-n的最长子序列为多少 
 // A 表示待求序列,n表示序列长度
int LIS_DP(int*A, int* D ,int n){
	int d[n + 2];
	memset(d, 0, sizeof(d));
	for(int i = 1; i <= n; i++){
		d[i] = 1;
		for(int j = i - 1; j >= 1; j--){
			if(A[i] > A[j]){
				if(d[j] + 1 > d[i]){ 
					d[i] = d[j] + 1;
				}
			}
		}
		if(D[i - 1] < d[i]) D[i] = d[i];// 记录最大值 
		else D[i] = D[i - 1]; 
	} 
	return D[n]; 
}

// 最长降序序列 
int LIS_DP2(int*A, int* D ,int n){
	int d[n + 2];
	memset(d, 0, sizeof(d));
	for(int i = n; i >= 1; i--){
		d[i] = 1;
		for(int j = i + 1; j <= n; j++){
			if(A[i] > A[j]){
				if(d[j] + 1> d[i]){ 
					d[i] = d[j] + 1;
				}
			}
		}
		if(D[i + 1] < d[i]) D[i] = d[i];// 记录最大值 
		else D[i] = D[i + 1]; 
	} 
	return D[1]; 
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦鸢MoYuan

谢谢投喂!!!QWQ!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值