题目
给定一个整数序列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源码