您的位置:首页 > 其它

最长上升子序列

2015-09-27 21:18 246 查看
算法在编程之美上有比较全面的介绍,第一种算法:

时间复杂度O(n2)O(n^2)

//假设在前i个元素中,最长的递增子序列长度为LIS[i]

//LIS[i+1] = max{1,LIS[k]+1},if for k<=i LIS[i+1]>LIS[k]

//总的时间复杂度为o(n2)

int LIS2(vector<int>&arr){

vector<int> dp(arr.size(),0);
//时间复杂度O(n2)
for(int i=0;i<arr.size();++i){
for(int j=0;j<i;++j){
if(arr[i] > arr[j] && dp[j] + 1 > dp[i] ){
dp[i] = dp[j]+1;
}
}
}
//时间复杂度 O(n)
int max_val =0;
for(int i=0;i<dp.size();++i){
if(dp[i]>max_val)
max_val = dp[i];
}
return max_val;
}


第二种做法,关键是增加一个记录数组,maxV[i]记录长度为i的最长上升子序列的最大值的最小值。

时间复杂度也是O(n2)O(n^2)。

//长度为1的递增子序列最大元素最小值为MaxV[1]
//长度为2的递增子序列最大元素最小值为MaxV[2]
//我们希望找到前i个元素中的一个递增子序列,使得这个递增子序列的最大元素比array[i+1]小,且长度尽量长。
int LIS(vector<int>& arr)
{
int currentLength = 1;
vector<int> maxV; //maxV[i]表示长度为i的最长递增子序列最大值的最小值
maxV.push_back( ~(1<<31)+1);//最小值
maxV.push_back(arr[0]); //初始化长度为1时最大值的最小值为第一个元素
vector<int> dp(arr.size(),1);//dp[i]记录arr中1到i位中的lis的最长长度
int len = 1;// LIS长度
for(int i = 1; i < arr.size(); ++i)
{
int j;
for(j= len;j>=0;--j){
if(arr[i]>maxV[j]){ //长度逐渐减小,因为希望尽可能长
dp[i] = j+1;   //更新lis的长度
break;
}
}

if(dp[i] > len)//表明最长上升序列长度增加了,即刚才在j=len时就退出了
{
++len;
maxV.push_back(arr[i]);
//maxV[++len]= arr[i];
}else //表明需要替换以前的值
{
maxV[j+1] = arr[i];
}
}
return len;
};


第三种算法, 因为存在
i<j
maxV[i]<maxV[j]
, 所以可以采用二分法加速,时间复杂度为O(nlogn)O(nlogn)

// write your code here cpp
#include<iostream>
#include<vector>
using namespace std;
#define LOCAL

//返回比x小的最大的数值的下标
int BinSearch(vector<int>& arr,  int x)
{
int left = 0, right = arr.size()-1;
int mid;
while(left < right -1)
{
mid = (left + right) / 2;
if(arr[mid] < x)
{
left = mid ;
}else
{
right = mid-1;
}
}
if(arr[right]<x)
return right;
return left;
};

int LIS(vector<int>& arr)
{
int currentLength = 1;
vector<int> maxV; //maxV[i]表示长度为i的最长递增子序列最大值的最小值
maxV.push_back( ~(1<<31)+1);//最小值
maxV.push_back(arr[0]); //初始化长度为1时最大值的最小值为第一个元素
vector<int> dp(arr.size(),1);//dp[i]记录arr中1到i位中的lis的最长长度
int len = 1;// LIS长度
for(int i = 1; i < arr.size(); ++i)
{
int j;
//将这里换成二分可以降低时间复杂度
j = BinSearch(maxV,arr[i]);//返回比当前值arr[i]小长度的下标
dp[i] = j+1;

if(dp[i] > len)//表明最长上升序列长度增加了,即刚才在j=len时就退出了
{
++len;
maxV.push_back(arr[i]);
//maxV[++len]= arr[i];
}else //表明需要替换以前的值
{
maxV[j+1] = arr[i];
}
}
return len;
};

int main(){

#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif

int n;
while(cin>>n){
int tmp;
vector<int> arr;
while(n--){
cin>>tmp;
arr.push_back(tmp);
}
cout<<LIS(arr)<<endl;
}
return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: