[编程之美] PSet2.16 求数组中最长的递增子序列
来源:互联网 发布:萧山网络问政 高桥 编辑:程序博客网 时间:2024/06/05 18:09
问题描述:
写一个时间复杂度尽可能低的程序,求一个一维数组中最长递增子序列的长度。
例如在序列1,-1,2,-3,4,-5,6,-7中,其最长的递增子序列的长度为4(如1,2,4,6)。
解答与思路:
这个题目与前面求一维数组中子数组之和最大值有点像,不过区别还是很明显,比如:子数组是数组中一串连续相邻的数字,而子序列不一定是相邻的,因此要得到[0-k]的子数组最大和,只要分析[0-(k-1)]的子数组最大和即可,而考虑[0-k]的子序列的最长长度,就不能只分析[0-(k-1)]中子序列的最长长度。
解法一:暴力枚举,对于每一个子序列,都判断是否能构成递增的,然后选取其中最大的。子序列有2^N种(对原数组的每个数,可以选择加入子序列也可以不选择),这样的算法复杂度O(2^N)。
解法二:暴力枚举,对于每一个个数组中的元素,判定后面某元素是否有元素大于目前元素,并寻找当前元素为首的对应元素最长子序列。该算法复杂度O(N^2),代码:
//方法二:对每个确定的i扫描一遍数组,寻找当前最长递增子序列长度。int findLIS(int Arr[] , int arrLen){int LIS = 0;for(int i=0 ; i<arrLen ; i++){int temp = Arr[i];int count = 0;for(int j=1 ; j<arrLen ; j++){if(Arr[j] > temp){temp = Arr[j];count++;}}if(count > LIS)LIS = count;}return LIS;}
解法三:动态规划法,假设目标数组的前i个元素中,最长递增子序列长度为LIS[i],考虑对于任意的arr[i+1]>arr[k],将会导致LIS[i+1]=max(1,LIS[k]+1),因为第i+1个元素本身可以直接接在LIS[k]长的子序列后面构成一个更长的子序列,于此同时arr[i+1]本身至少可以构成一个长度为1的子序列(用于下次增加长度之前的初始化)。这种方法空间复杂度O(N),时间复杂度O(N^2),代码:
//方法三:动态规划法//对于LIS[i],有如果arr[i+1]>arr[k],则LIS[i+1] = max{1,LIS[k]+1}//因为LIS下标从0开始,定义LIS[i]为前i+1个元素的最长递增子序列长度int findLIS(int Arr[] , int arrLen){int *LIS = new int[arrLen];for(int i=0 ; i<arrLen ; i++){LIS[i] = 1;for(int k=0 ; k<i ; k++){if(Arr[i]>Arr[k] && LIS[i]<LIS[k]+1){//寻找最大的LIS[k]+1赋值给LIS[i]LIS[i] = LIS[k]+1;}}}//---寻找LIS中最大值int maxLIS = -9999;for(int j=0 ; j<arrLen ; j++){if(maxLIS < LIS[j])maxLIS = LIS[j];}delete []LIS;LIS = NULL;return maxLIS;}
解法四:维护不同长度的递增子序列的最大元素最小值MaxV[LIS[i]],可以考虑找到前i个元素中的一个递增子序列,使得这个递增子序列的最大元素比arr[i+1]小,且长度尽可能长。由于else的部分需要逆序遍历,因此时间复杂度还是O(N^2),不过比上面的方法会快一些。代码:
//方法四:考虑前面i个元素的分布情况//定义MaxV[i]为长度为i的递增子序列最大元素的最小值,int findLIS(int Arr[] , int arrLen){int *MaxV = new int[arrLen+1];// 递增子序列最长不超过arrLenint maxLen = 1;MaxV[0] = INT_MIN;MaxV[1] = Arr[0];for(int i=1 ; i<arrLen ; i++){//从Arr[1]遍历到Arr[N-1]if(Arr[i] > MaxV[maxLen]){maxLen++;MaxV[maxLen] = Arr[i];}else{//否则逆序寻找能否有MaxV[j]<Arr[i]<MaxV[j+1],此时可以更新MaxV[j+1]=Arr[i]int j=maxLen-1;while(Arr[i]<MaxV[j])//MaxV[0]作为哨兵,为-INFj--;//跳出循环时Arr[i]>MaxV[j],当前子数组最小的数更新MaxV[1]的值。MaxV[j+1] = Arr[i];}}delete []MaxV;MaxV = NULL;return maxLen;}解法四改进:由于是递增序列,因此对于i<j一定有MaxV[i]<MaxV[j]。依据单调递增的关系,考虑使用二分搜索进行加速,将时间复杂度降低为O(NlogN)。代码如下:
//方法四改进:将穷举逆序搜索MaxV[j]<Arr[i]<MaxV[j+1]改为二分搜索//复杂度提高为O(NlogN)int findLIS(int Arr[] , int arrLen){int *MaxV = new int[arrLen+1];// 递增子序列最长不超过arrLenint maxLen = 1;MaxV[0] = INT_MIN;MaxV[1] = Arr[0];for(int i=1 ; i<arrLen ; i++){//从Arr[1]遍历到Arr[N-1]if(Arr[i] > MaxV[maxLen]){maxLen++;MaxV[maxLen] = Arr[i];}else{//二分搜索MaxV[j]<Arr[i]<MaxV[j+1],注意MaxV[0]可以更新MaxV[1]为合理的更小值int b = 0;int e = maxLen-1;while(b<e){int mid = b+((e-b)>>1);//防止上溢if(MaxV[mid] < Arr[i])b=mid+1;elsee=mid-1;}//跳出循环说明找到这样的地方或未能找到if(MaxV[b] < Arr[i])MaxV[b+1] = Arr[i];}}delete []MaxV;MaxV = NULL;return maxLen;}int main(void) { int a[8] = {1, -5, 2, -4, 4, -3, -2, -1}; cout<<findLIS(a , sizeof(a)/sizeof(int))<<endl;return 0; }
- [编程之美] PSet2.16 求数组中最长的递增子序列
- [编程之美]求数组中最长递增子序列
- 编程之美--求数组中最长递增子序列
- 编程之美--求数组中最长递增子序列
- 编程之美读书笔记_2.16 求数组中最长递增子序列
- 编程之美 求数组中的最长递增子序列
- 读书笔记之编程之美 - 2.16 求数组中最长递增子序列
- 编程之美读书笔记之2.16求数组中最长递增子序列
- 编程之美: 第二章 数字之魅 2.16求数组中最长递增子序列
- 《编程之美》读书笔记17: 2.16 求数组中最长递增子序列
- 编程之美2.16 求数组中最长递增子序列
- 编程之美2.16——求数组中最长递增子序列
- 编程之美2.16求数组中最长递增子序列
- 编程之美_012求数组中最长递增子序列
- 求数组中最长递增子序列—动态规划入门(编程之美)
- POJ 2533 Longest Ordered Subsequence 编程之美 2.16 求数组中最长递增子序列
- 编程之美---求数组中最长递增子序列LIS
- 编程之美 2.16 求数组中最长递增子序列
- Ubuntu Linux下为PHP5安装cURL
- 23-编码实现软件界面与通知
- 建站:域名注册,从国外注册干净的域名,用虚拟信用卡取得最优价格
- UVA - 10790 How Many Points of Intersection?
- sed高级用法
- [编程之美] PSet2.16 求数组中最长的递增子序列
- 算法导论 第7章 快速排序
- Swt中实现对TitleAreaDialog窗口的关闭进行监听
- C++ 多态机制浅析
- Linux内核的ioctl函数学习
- Hdu1176 - 免费馅饼 - 动态规划
- 7.jQuery UI 邮箱自动补全
- 树的三种遍历
- sed & awk单行脚本快速参考