算法题10 最长等差序列问题
来源:互联网 发布:为什么江老是被黑 知乎 编辑:程序博客网 时间:2024/06/01 12:16
给定一个大小为n的数组,要求写出一个算法,求其最长的等差数列的子序列
分析
该题需要分几种情况考虑。
1. 原数组是有序的,所要求的的子序列可以不连续。
对于数组arr[],不同的等差值d=1,2,3,4,5```(arr[max]-arr[min])可以求出不同的最长等差数列,然后在这些等差数列中求出最长的那个即可我们首先转化为求一个数组的固定等差值的最长等差子序列。如数组1,2,4,6,8,9,求等差值2的最长等差子序列为2,4,6,8
1.1 固定等差值的最长子序列
求符合条件的最长子序列可以用动态规划来做,设dis[i]记录数组arr[]的第1-i个元素子数组的最长等差子序列长度,状态转移方程式:
dis[i]=1;
dis[i]=dis[k]+1, k是数组第1-i个元素中,与元素i等差为d并且距离i最近的元素(k<i);
代码
1 int RegularArithmeticSeq(int arr[],int len,int d) 2 { 3 if (arr==NULL||len<1) 4 throw std::exception("Invalid input."); 5 6 int* dis=new int[len+1](); 7 8 int re_len=0,re_index=0; 9 for (int i=1;i<=len;i++)10 {11 dis[i]=1;12 for (int k=i;k>0;k--)13 { 14 int cur_d=arr[i-1]-arr[k-1];15 16 if (cur_d==d&&dis[i]<=dis[k]) 17 {18 dis[i]=dis[k]+1;19 }20 21 if (re_len<=dis[i])22 {23 re_len=dis[i];24 re_index=i;25 }26 27 if (cur_d>d)28 break;29 }30 }31 32 int out=arr[re_index-1];33 for (int n=0;n<re_len;n++)34 {35 cout<<out<<' ';36 out=out-d;37 }38 cout<<endl;39 40 delete[] dis;41 42 return re_len;43 }
上述代码的时间复杂度为O(n*n),时间复杂度为O(n);
对于寻找1-i中最后一个和i等差的元素k,除了顺序遍历,还可以用二分查找法进行一点优化,查找的时间复杂度为O(logn)
1 int find_k(int arr[],int n,int d) 2 { 3 int small=0,big=n-1,k=0,sum=0; 4 while (small<=big) 5 { 6 k=small+(big-small)/2; 7 sum=arr[k]+d; 8 if (sum<arr[n-1]) 9 {10 small=k+1;11 }else if(sum>arr[n-1])12 {13 big=k-1; 14 }else15 {16 if (k<=big&&(arr[k+1]+d)!=sum)17 {18 return k;19 }else20 {21 small=k+1;22 }23 }24 }25 26 return n-1;27 }
优化代码
1 int RegularArithmeticSeq2(int arr[],int len,int d,int& re_idx) 2 { 3 if (arr==NULL||len<1) 4 throw std::exception("Invalid input."); 5 6 int* dis=new int[len](); 7 8 int* dif=new int[len](); 9 for (int i=0;i<len;i++)10 {11 dif[i]=find_k(arr,i+1,d);12 }13 14 int re_len=0,re_index=0;15 for (int i=0;i<len;i++)16 {17 dis[i]=1;18 if (dif[i]!=i)19 {20 dis[i]=dis[dif[i]]+1;21 }22 if (re_len<=dis[i])23 {24 re_len=dis[i];25 re_index=i;26 }27 }28 29 int out=arr[re_index];30 re_idx=re_index+1;31 for (int n=0;n<re_len;n++)32 {33 cout<<out<<' ';34 out=out-d;35 }36 cout<<endl;37 38 delete[] dis;39 delete[] dif;40 41 return re_len;42 }
上述过程中时间复杂度降为O(n*logn)
1.2 非固定等差值的最长子序列
在解决了固定等差值得最长子序列后,就可以着手求非固定等差值的最长子序列了。等差值d的范围是1,2···(arr[max]-arr[min]),循环着m个等差值,然后求出最长的等差子序列即可
时间复杂度(n*logn*m),m<=n-1;空间复杂度O(n*m)
代码
1 int ArithmeticSeq(int arr[],int len) 2 { 3 if (arr==NULL||len<1) 4 throw std::exception("Invalid input."); 5 6 int max_d=arr[len-1]-arr[0]; 7 int re_len=0,re_d=0,re_index=0; 8 9 for (int d=1;d<=max_d;d++)10 {11 int idx=0;12 int dlen=RegularArithmeticSeq2(arr,len,d,idx);13 if(re_len<=dlen)14 {15 re_len=dlen;16 re_d=d;17 re_index=idx;18 }19 }20 21 int out=arr[re_index-1];22 for (int n=0;n<re_len;n++)23 {24 cout<<out<<' ';25 out=out-re_d;26 }27 cout<<endl;28 29 return re_len;30 }
也有另外一种解法:设dis[i][d]为数组中第1到i个元素中等差值为d的最长子序列的长度(序列可以不连续),则动态规划式;
dis[i][d]=1,默认等1,即在1-i个元素中等差值为d的最长子序列长度至少是1(i元素本身)
dis[i][d]=dis[k][d]+1, k为1-i个元素中最后一个与i元素的等差d的元素
代码
1 int ArithmeticSeq(int arr[],int len) 2 { 3 if (arr==NULL||len<1) 4 throw std::exception("Invalid input."); 5 6 int max_d=arr[len-1]-arr[0]; 7 int** dis=new int*[len+1]; 8 for (int i=0;i<=len;i++) 9 {10 dis[i]=new int[max_d+1]();11 dis[i][0]=1;12 }13 14 int re_len=0,re_d=0,re_index=0;15 for (int i=1;i<=len;i++)16 {17 for (int d=1;d<=max_d;d++)18 {19 dis[i][d]=1;20 for (int k=1;k<i;k++)21 { 22 int cur_d=arr[i-1]-arr[k-1];23 if (cur_d==d&&dis[i][d]<=dis[k][d]) 24 {25 dis[i][d]=dis[k][d]+1;26 }27 28 if (re_len<=dis[i][d])29 {30 re_len=dis[i][d];31 re_d=d;32 re_index=i;33 }34 }35 36 }37 }38 39 int out=arr[re_index-1];40 for (int n=0;n<re_len;n++)41 {42 cout<<out<<' ';43 out=out-re_d;44 }45 cout<<endl;46 47 for (int i=0;i<=len;i++)48 {49 delete[] dis[i];50 }51 delete[] dis;52 53 return re_len;54 }
2. 原数组是无序的,所要求的的子序列可以不连续。
网上有不用排序的解法,但笔者觉得先排序还是更好些,也容易理解。当然,解此题的关键是找到在循环i和d的过程中找到k,对于非排序数组用顺序查找k,也可以。
代码略
3. 原数组是无序的,所要求的的子序列连续。
1 int ArithmeticSeq3(int arr[],int len) 2 { 3 if (arr==NULL||len<1) 4 throw std::exception("Invalid input."); 5 6 int re_len=1,d=1; 7 int max_len=0,re_d=0,re_index=0; 8 for (int i=1;i<len;i++) 9 {10 if((arr[i]-arr[i-1])==d)11 {12 re_len=re_len+1;13 }else14 {15 re_len=2; 16 }17 d=arr[i]-arr[i-1];18 if (max_len<=re_len)19 {20 max_len=re_len;21 re_d=d;22 re_index=i;23 }24 }25 26 int out=arr[re_index];27 for (int n=0;n<max_len;n++)28 {29 cout<<out<<' ';30 out=out-re_d;31 } 32 cout<<endl;33 34 return re_len;35 }
- 算法题10 最长等差序列问题
- 最长等差子序列
- 求最长单调递增【等差】子序列
- NOJ 1526 最长等差子序列
- 关于最长等差子列的问题
- 最长公共子序列算法问题
- 算法实现-->最长子序列问题
- 《算法导论》最长子序列问题
- codevs1283等差子序列
- bzoj2124 等差子序列
- BZOJ2124 等差子序列
- BZOJ2124: 等差子序列
- 2124: 等差子序列
- 算法题:最长公共子序列
- 算法题:最长上升子序列
- 经典算法题06-最长子序列
- 【算法题】最长回文子序列
- 【算法题】最长公共子序列
- 算法题7 判断扑克牌中的顺子
- 算法题8 动态规划之字符串相似度
- 309.Best Time to Buy and Sell Stock with Cooldown
- 算法题9 动态规划之最长公共子序列&最长公共子串
- 死锁、活锁、优先级翻转
- 算法题10 最长等差序列问题
- Android 中 onTouch 和OnClick 冲突的处理(onTouchEvent返回true时与onclick冲突)
- 算法题11 字符串的所有对称子串
- CodeForces 622C Not Equal on a Segment
- 算法题12 数组中所有的逆序对
- Android设计模式系列(2)--SDK源码之观察者模式
- 算法题13 排序算法(更新快排)
- iOS单元测试
- 算法题14 最低公共祖先