算法——最长递增子序列(LIS)

本文介绍了一种求解最长递增子序列(LIS)问题的有效算法,通过使用栈及改进的方法不仅能够求得LIS的长度,还能输出完整的LIS序列。文中详细解释了算法原理,并提供了一个具体的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最长递增子序列描述的是这样一个问题——给定一个混乱序列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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值