给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。LIS(longestIncreasingSubsequence)
说明:
给出 [5,4,1,2,3],LIS 是 [1,2,3],返回 3
给出 [4,2,4,5,3,7],LIS 是 [2,4,5,7],返回 4
要求时间复杂度为O(n^2) 或者 O(nlogn)
//在非递减序列 arr[s..e](闭区间)上二分查找第一个大于等于key的位置,如果都小于key,就返回e+1
int upper_bound(int arr[], int s, int e, int key)
{
int mid;
if (arr[e] <= key)
return e + 1;
while (s < e)
{
mid = s + (e - s) / 2;
if (arr[mid] <= key)
s = mid + 1;
else
e = mid;
}
return s;
}
int LIS(int d[], int n)
{
int i = 0, len = 1, *end = (int *)alloca(sizeof(int) * (n + 1));
end[1] = d[0]; //初始化:长度为1的LIS末尾为d[0]
for (i = 1; i < n; i++)
{
int pos = upper_bound(end, 1, len, d[i]); //找到插入位置
end[pos] = d[i];
if (len < pos) //按需要更新LIS长度
len = pos;
}
return len;
}
class Solution { public: /** * @param nums: The integer array * @return: The length of LIS (longest increasing subsequence) */ int longestIncreasingSubsequence(vector<int> nums) { // write your code here int n=nums.size(); int dp[n]; if(n==0){ return 0; } memset(dp,0,sizeof(int)*n); int len=1; dp[0]=nums[0]; for(int i=1;i<n;i++){ int pos=lower_bound(dp,dp+len,nums[i])-dp; dp[pos]=nums[i]; len=max(len,pos+1); } return len; } };
在第二种方法中,我们花费了很多时间在寻找最大的dp[j]上。如果有办法让这个dp[j]变成一个递增的序列,我们就能使用二分来进行优化,从而使得复杂度下降为O(nlogn)了。
幸
运的是,这种方法确实是存在的。我们可以使用dp[i]来保存在前i个数中最大的那个数,很容易可以理解,这个dp[i]已经是单调不减的。接下来的处理
其实有些贪心的思想,对于每一个a[i],我们都在dp数组中寻找比它大的第一个数的下标,不妨设为pos,然后用a[i]来更新dp[pos]。于是我
们可以明白,len就应当是max(len, pos+1)。
在这里我们使用lower_bound函数,这个函数将会返回小于等于val的第一个值的指针,如果不存在就返回end指针。
原文:http://www.cnblogs.com/Allen-rg/p/5835807.html