最长递增子序列

题目

给定一个整数序列arr,长度为N,找到最长上升子序列(LIS),返回LIS的长度。

说明

最长上升子序列的定义:

最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。

样例

给出 [5,4,1,2,3],LIS 是 [1,2,3],返回 3
给出 [4,2,4,5,3,7],LIS 是 [4,4,5,7],返回 4

分析

本题也是一个动态规划算法的典型应用。需要维护一个长度为N的数组dp;dp[i]代表在以arr[i]这个数 为结尾的情况下,arr[0...i]中的最长递增子序列长度。

方法一:复杂度O(n^2)求解数组dp

  • dp[0] = 1,接下来从左向右依次算出以每个位置的数结尾的情况下,最长递增子序列长度;
  • 假设计算到位置i,求以arr[i]为结尾的情况下最长递增子序列长度dp[i]。如果最长递增子序列以arr[i]结尾,那么在arr[0...i-1]中,所有比arr[i]小的数都可以作为倒数第二个数。这么多倒数第二个数的选择中,以哪个数结尾的最长递增子序列长度更大,就选那个数作为倒数第二数。所以,dp[i] = max(dp[j]+1, (0 <= j < i && arr[j] < arr[i]));
  • 如果arr[0...i-1]中的数都大于arr[i],则dp[i] = 1:

方法二:

在上个方法中,我们用线性复杂度求出arr[0...i-1]中作为倒数第二个数。此步骤可以利用二分的思想降低到O(logn);

代码

/*
4.5 最长递增子序列
给定一个整数序列arr,长度为N,找到最长上升子序列(LIS),返回LIS的长度。

举例:
给出 [5,4,1,2,3],LIS 是 [1,2,3],返回 3
给出 [4,2,4,5,3,7],LIS 是 [4,4,5,7],返回 4
*/
#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>

using namespace std;

//O(n^2)的复杂度
vector<int> getDp1(vector<int> &nums)
{
	if (nums.empty())
	{
		return vector<int>();
	}//if

	int len = nums.size();
	vector<int> dp(len, 0);
	for (int i = 0; i < len; ++i)
	{
		dp[i] = 1;
		for (int j = 0; j < i; ++j)
		{
			if (nums[i] > nums[j])
			{
				dp[i] = max(dp[i], dp[j] + 1);
			}//if
		}//for
	}//for

	return dp;
}

//O(nlogn)的复杂度(针对不含重复元素的最长递增子序列)
vector<int> getDp2(vector<int> &nums)
{
	if (nums.empty())
	{
		return vector<int>();
	}//if

	int len = nums.size();
	vector<int> dp(len, 0), ends(len, 0);
	dp[0] = 1;
	ends[0] = nums[0];

	int right = 0;

	int l = 0, r = 0, m = 0;
	for (int i = 1; i < len; ++i)
	{
		l = 0;
		r = right;
		/*采用二分的思想,降低复杂度*/
		while (l <= r)
		{
			m =l +  (r - l) / 2;
			if (nums[i] > ends[m])
			{
				l = m + 1;
			}//if
			else {
				r = m - 1;
			}//else
		}//while

		right = max(right, l);
		ends[l] = nums[i];
		dp[i] = l + 1;
	}//for

	return dp;
}

//动态规划
int longestIncreasingSubsequence(vector<int> nums) {
	// write your code here
	if (nums.empty())
	{
		return 0;
	}//if

	vector<int> dp = getDp1(nums);
	int maxLen = 0, len = dp.size();
	for (int i = 0; i < len; ++i)
	{
		if (dp[i] > maxLen)
		{
			maxLen = dp[i];
		}//if
	}//for

	return maxLen;
}


int main()
{
	vector<int> v = { 5,4,1,2,3 };
	cout << longestIncreasingSubsequence(v) << endl;

	system("pause");
	return 0;
}
GitHub源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值