每日一题(79) - 求数组中最长递增子序列

来源:互联网 发布:脉冲信号测试软件 编辑:程序博客网 时间:2024/06/08 17:05

题目来自编程之美

题目


思路(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;//寻找LISnLastNumPos = 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;}

原创粉丝点击