题目来自编程之美
题目
思路(1) 动态规划(复杂度为n^2)
方程:
F[i]:表示以nArr[i]为结尾的最长递增子序列的最大长度。
F[i] = Max(F[j]) + 1 && a[i] > a[j] && i > j;
方程的直观含义:检查nArr[i]能接在哪一个数的后面。
如果要求LIS序列,需要令开辟一个数组。
复杂度:O(n^2)
代码:只求LIS的长度
#include <iostream>
#include <assert.h>
using namespace std;
/*
F[i]:表示以nArr[i]为结尾的最长递增子序列的长度
F[i] = Max(F[j]) + 1 && a[i] > a[j] && i > j;
F[i] = 1
*/
int LIS(int nArr[],int nLen)
{
int nMaxLen = 1;
int* F = new int[nLen];
//递推
for (int i = 1;i < nLen;i++)
{
F[i] = 1;
for (int j = i - 1;j >= 0;j--)
{
if (nArr[i] > nArr[j])
{
F[i] = max(F[i],F[j] + 1);
}
}
nMaxLen = max(nMaxLen,F[i]);
}
//输出
return nMaxLen;
}
int main()
{
int nArr[8] = {1,-1,2,-3,4,-5,6,-7};
cout<<LIS(nArr,8)<<endl;
system("pause");
return 1;
}
代码:输出一个LIS序列
#include <iostream>
#include <assert.h>
using namespace std;
void Print(int* nArr,int* Pre,int nCurPos)
{
if (nCurPos == -1)
{
return;
}
Print(nArr,Pre,Pre[nCurPos]);
cout<<nArr[nCurPos]<<" ";
}
/*
F[i]:表示以nArr[i]为结尾的最长递增子序列的长度
F[i] = Max(F[j]) + 1 && a[i] > a[j] && i > j;
F[i] = 1
*/
int LIS(int nArr[],int nLen)
{
int nMaxLen = 1;
int* F = new int[nLen];
int* Pre = new int[nLen];
int nMaxPos = -1;
//递推
for (int i = 1;i < nLen;i++)
{
F[i] = 1;
Pre[i] = -1;
for (int j = i - 1;j >= 0;j--)
{
if (nArr[i] > nArr[j])
{
if (F[i] < F[j] + 1)
{
F[i] = F[j] + 1;
Pre[i] = j;
}
}
}
if (nMaxLen < F[i])
{
nMaxLen = F[i];
nMaxPos = i;
}
}
//输出
Print(nArr,Pre,nMaxPos);
cout<<endl;
return nMaxLen;
}
int main()
{
int nArr[8] = {1,-1,2,-3,4,-5,6,-7};
cout<<LIS(nArr,8)<<endl;
system("pause");
return 1;
}
思路(2)O(nlogn)的算法
思路:数组nArrIncr中的数据都是不重复的。
代码:
int BinSearch(int nArrIncr[],int nNum,int nStart,int nEnd)
{
int nMid = -1;
while(nStart <= nEnd)
{
nMid = (nStart + nEnd) >> 1;
if (nArrIncr[nMid] == nNum)
{
return nMid;//返回nNum的位置
}
else if (nArrIncr[nMid] > nNum)
{
nEnd = nMid - 1;
}
else
{
nStart = nMid + 1;
}
}
return nStart; //返回第一个大于nNum的位置
}
int LIS(int nArr[],int nLen)
{
assert(nLen > 0);
int nEnd = 0;
int* nArrIncr = new int[nLen];
nArrIncr[0] = nArr[0];
int nPos = -1;
for (int i = 1;i < nLen;i++)
{
if (nArr[i] > nArrIncr[nEnd])
{
nArrIncr[++nEnd] = nArr[i];
}
else
{
nPos = BinSearch(nArrIncr,nArr[i],0,nEnd);//寻找第一个大于等于nArr[i]的数
nArrIncr[nPos] = nArr[i];
}
}
return nEnd + 1;
}
利用STL,给出一个更简洁的代码:
int LIS(int nArr[],int nLen)
{
assert(nLen > 0);
int nEnd = 0;
int* nArrIncr = new int[nLen];
nArrIncr[0] = nArr[0];
int nPos = -1;
for (int i = 1;i < nLen;i++)
{
if (nArr[i] > nArrIncr[nEnd])
{
nArrIncr[++nEnd] = nArr[i];
}
else
{
//寻找第一个大于等于nArr[i]的数
*lower_bound(nArrIncr,nArrIncr + nEnd + 1,nArr[i]) = nArr[i];
}
}
return nEnd + 1;
}
注:
lower_bound作用:在区间[first,last)进行二分查找,返回第一个大于或等于val的元素位置(迭代器)。
(1)如果所有元素都小于val,则返回last的位置,且last的位置是越界的
(2)头文件:#include <algorithm>
输出一个LIS,复杂度为O(n)
代码
#include <iostream>
#include <assert.h>
#include <algorithm>
using namespace std;
void Print(int nArr[],int nCurLen,int nCurPos,int nArrPos[])
{
if (nCurLen == 0)
{
return;
}
if (nCurLen == nArrPos[nCurPos])
{
Print(nArr,nCurLen - 1,nCurPos - 1,nArrPos);
cout<<nArr[nCurPos]<<" ";
}
else
{
Print(nArr,nCurLen,nCurPos - 1,nArrPos);
}
}
void LIS(int nArr[],int nLen)
{
assert(nLen > 0);
int nEnd = 0;
int* nArrIncr = new int[nLen];
int* nArrPos = new int[nLen];
nArrIncr[0] = nArr[0];
nArrPos[0] = 1; //每一个元素所在LIS的位置
int nLastNumPos = 0;//LIS最后一个元素的下标
for (int i = 1;i < nLen;i++)
{
if (nArr[i] > nArrIncr[nEnd])
{
nArrIncr[++nEnd] = nArr[i];
nArrPos[i] = nEnd + 1;//寻找LIS
nLastNumPos = i;//记录LIS最后一个元素的位置
}
else
{
//寻找第一个大于等于nArr[i]的数
int nPos = lower_bound(nArrIncr,nArrIncr + nEnd + 1,nArr[i]) - nArrIncr;
nArrIncr[nPos] = nArr[i];
nArrPos[i] = nPos + 1;//记录每一个数是在LIS中第几个数
}
}
//输出信息
cout<<"长度: "<<nEnd + 1<<endl;
cout<<"LIS: ";
Print(nArr,nEnd + 1,nLastNumPos,nArrPos);
cout<<endl;
}
int main()
{
//int nArr[8] = {1,-1,2,-3,4,-5,6,-7};
//LIS(nArr,8);
int nArr[1] = {-7};
LIS(nArr,1);
//int nArr[8] = {-7,10,9,2,3,8,8,1};
//LIS(nArr,8);
system("pause");
return 1;
}