关于序列的几个算法

来源:互联网 发布:vb代码大全 编辑:程序博客网 时间:2024/05/16 06:11

1.求最小子序列的和

就是对于连续的序列,找出连续序列中和最小的

例如:int a[LEN] = {4,-1,5,-2,-1,2,6,-2,1,-3};

最小的子序列就是:-2,1,-3

对于下面的最大子序列就是:4,-1,5,-2,-1,2,6。

 

 

Cpp代码  收藏代码
  1. /** 
  2.     *最小子序列和 
  3.     *n 
  4.     */  
  5. int subMinSum(int a[], int length){  
  6.     int thismin = 0, min = INT_MAX;  
  7.     for(int i=0; i<length; i++){  
  8.         thismin += a[i];  
  9.         if(thismin < min){  
  10.             min = thismin;  
  11.         }else if(thismin > 0){  
  12.             thismin = 0;  
  13.         }  
  14.     }  
  15.     return min;  
  16. }  

 

 2.最大子序列的和,其思想和求最小是一样的

 

Cpp代码  收藏代码
  1. /** 
  2.     *最大子序列和 
  3.     * 
  4.     */  
  5. int subMaxSum(int *a, int length){  
  6.     int thismax = 0, max=INT_MIN;  
  7.     for(int i=0; i<length; i++){  
  8.         thismax += a[i];  
  9.         if(thismax > max){  
  10.             max = thismax;  
  11.         }else if(thismax < 0){  
  12.             thismax = 0;  
  13.         }  
  14.     }  
  15.     return max;  
  16. }  

 

 针对上面的两种算法,效率是比较高的,但是正确性不容易看出来,需要自己仔细的揣摩和证明,但是优点也很明显,就是对数据只需要一次的扫描,顺序读入,这对于大量数据来说是很有利的,只要全部读入数据就可以得出结果,这个在算法分析一书中叫做“联机算法(on-line algorithm)”,意思就是上面描述的,线性时间完成的联机算法基本上算法完美算法。

 

3.求最大子序列,并记录子序列位置

这里我们肯定很容易记录下最终的位置,就是不容易确定序列的起始位置,主要是由于该算法的特性,最后想了好久才想到用:减最大的值的方法,当等于0的时候就找到起始位置了,从而确定序列范围

 

Cpp代码  收藏代码
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int maxsub(const int* a, int length, int* loc){  
  5.     //maxsum记录最大的值,thissum记录当前的值  
  6.     int maxsum=0, thissum=0, i=0;  
  7.     if(length <= 0) return 0;  
  8.     for(i=0; i<length; i++){  
  9.         thissum += a[i];  
  10.         if(maxsum < thissum){  
  11.             maxsum = thissum;  
  12.             loc[1] = i;  
  13.             //这里有一个思想就是,为负数的子序列不可能成为最优子序列的前缀,  
  14.         }else if(thissum < 0){  
  15.             thissum = 0;  
  16.         }  
  17.     }  
  18.     thissum = maxsum;  
  19.     for(i=loc[1]; i>=0; i--){  
  20.         thissum -= a[i];  
  21.         if(thissum == 0){  
  22.              loc[0] = i;  
  23.              break;  
  24.         }  
  25.     }  
  26.     return maxsum;  
  27. }  
  28.   
  29. int main(){  
  30.     int a[] = {2, -3, 7, -4,-2,-2,6,-2};  
  31.     int loc[2]={0};  
  32.     cout<<maxsub(a, 8, loc)<<endl;  
  33.     cout<<"from:"<<loc[0]<<"---to:"<<loc[1]<<endl;  
  34.     return 0;     
  35. }  
 

 

 

4.最小正子序列和

这个是在一博客中看到的:http://blog.csdn.net/qq675927952/article/details/6790726

然后我有一个疑问就是,究竟什么是最小正子序列和?这个在网上找了也没有个好的答案(http://tengtime.com/a/gaoxingnenWEBkaifa/20120406/3217.html),如果哪位仁兄知道,不胜感激!!

对于下面求出的sum我也没搞的太清楚是为什么?然后又循环什么的?

 

Cpp代码  收藏代码
  1. struct Node    
  2. {    
  3.     int sum;    
  4.     int xiabiao;    
  5.         
  6. };    
  7. int cmp(const Node& t1,const Node& t2)    
  8. {    
  9.     return t1.sum < t2.sum;  
  10. }   
  11.   
  12. /** 
  13.     *最小正子序列和 
  14.     *n*logn 
  15.     */  
  16. int positiveSubMinSum(int *data, int len){  
  17.     Node* temp = new Node[len];    
  18.   temp[0].sum = data[0];    
  19.   temp[0].xiabiao = 0;    
  20.   for(int i=1;i<len;i++)    
  21.   {    
  22.     temp[i].sum = temp[i-1].sum+data[i];    
  23.     temp[i].xiabiao = i;    
  24.   }    
  25.   //对temp.sum[]进行从小到大排序,sum[]中只有相邻的两个数才有可能 得到 最小正子序列和    
  26.   sort(temp,temp+len,cmp);    
  27.   int sum = INT_MAX;    
  28.   for(int i=0;i<len-1;i++)    
  29.   {    
  30.     if(temp[i].xiabiao < temp[i+1].xiabiao)    
  31.     {    
  32.          if(temp[i+1].sum - temp[i].sum > 0 && temp[i+1].sum - temp[i].sum < sum)    
  33.          sum = temp[i+1].sum - temp[i].sum;    
  34.     }    
  35.   }    
  36.   delete temp;    
  37.   temp=0;    
  38.   return sum;    
  39. }  

  5.最大子序列的乘积

对网上的一些算法进行了下比较,发现这个算法还可以,比较简洁:http://blog.csdn.net/ztj111/article/details/1909339

对该算法的说明:

 

这个问题其实可以简化成这样:数组中找一个子序列,使得它的乘积最大;同时找一个

子序列,使得它的乘积最小(负数的情况)。虽然我们只要一个最大积,但由于负数的

存在,我们同时找这两个乘积做起来反而方便。

 

我们让maxCurrent表示当前最大乘积的candidate,minCurrent反之,表示当前最小乘积

的candidate。这里我用candidate这个词是因为只是可能成为新一轮的最大/最小乘积,

而maxProduct则记录到目前为止所有最大乘积candidates的最大值。

 

由于空集的乘积定义为1,在搜索数组前,maxCurrent,maxProduct,minCurrent都赋为1。

假设在任何时刻你已经有了maxCurrent和minCurrent这两个最大/最小乘积的candidates,

新读入数组的元素x(i)后,新的最大乘积candidate只可能是maxCurrent或者minCurrent

与x(i)的乘积中的较大者,如果x(i)<0导致maxCurrent<minCurrent,需要交换这两个

candidates的值。

 

当任何时候maxCurrent<1,由于1(空集)是比maxCurrent更好的candidate,所以更新

maxCurrent为1,类似的可以更新minCurrent。任何时候maxCurrent如果比最好的

maxProduct大,更新maxProduct。

 

 

Cpp代码  收藏代码
  1. void swap(int& a, int& b){  
  2.      int temp = a;  
  3.      a = b;  
  4.      b = temp;  
  5. }  
  6.   
  7. /** 
  8.     *最大子序列乘积(同时也求出了最小的子序列乘积) 
  9.     *在找最大的值得时候,必须记录最小值,因为有负数存在,最小的数可能变成最大的数 
  10.     */  
  11. int mutiSubMax(int *a, int length){  
  12.       int i;  
  13.     int maxProduct = 1;  
  14.     int minProduct = 1;  
  15.     int maxCurrent = 1;  
  16.     int minCurrent = 1;  
  17.   
  18.     for( i=0; i<length ;i++)  
  19.     {  
  20.         maxCurrent *= a[i];  
  21.         minCurrent *= a[i];  
  22.         if(maxCurrent > maxProduct)   
  23.             maxProduct = maxCurrent;  
  24.         if(minCurrent > maxProduct)  
  25.             maxProduct = minCurrent;  
  26.         if(maxCurrent < minProduct)  
  27.             minProduct = maxCurrent;  
  28.         if(minCurrent < minProduct)  
  29.             minProduct = minCurrent;  
  30.         //注意交换  
  31.         if(minCurrent > maxCurrent)  
  32.             swap(maxCurrent,minCurrent);  
  33.         //这个必须在最后(防止为0的时候)  
  34.         if(maxCurrent<1)  
  35.             maxCurrent = 1;  
  36.         //if(minCurrent>1)//这里不需要,因为通过交换即可,只需要一个  
  37.         //  minCurrent =1;  
  38.     }  
  39.     return maxProduct;  
  40. }  
 

6.最长递增子序列
参考:

http://blog.csdn.net/hhygcy/article/details/3950158

http://www.programfan.com/blog/article.asp?id=13086

a,问题描述

L=<a1,a2,…,an>n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<kmaK1<ak2<…<akm。求最大的m

b,问题分析

最长递增子序列可以看成一个动态规划的问题,关于动态规划可以参见:

http://hi.baidu.com/hacklzt/blog/item/81e6b8fc795d251f09244de7.html

http://www.cnblogs.com/brokencode/archive/2011/06/26/2090702.html

其实质就是:将问题细化,逐步求解

当然实际的过程相对复杂些,就拿该题来说

如:子序列{1, 9, 3, 8, 11, 4, 5, 6, 4, 19, 7, 1, 7 }这样一个字符串的的最长递增子序列就是{1,3,4,5,6,7}或者{1,3,4,5,6,19}。

首先,该问题可以抽象为:子序列为L(1..n),  A(1...i)是一个从1到i的优化的子结构,也就是最长递增子序列,求A(n)。

其次,根据上面的抽象,求的就是A(n), 那么问题就转化为求A(j)与A(1...i)(j>i)的关系(状态转移方程)。

最后,根据动态规划的概念,找的就是上面的这样一个关系,如何将A(j)与A(1...i)联系起来?从而将问题细化,L(j) = max {L(i), i<j && A(i)<A(j) } + 1

也就是说L(j)等于之前所有的L(i)中最大的的L(i)加一。这样的L(i)需要满足的条件就是A(i)<A(j).这个推断还是比较容易理解的.就是选择j之前所有的满足小于当前数组的最大值.

然后就是将上面的关系式转换为代码,这个就很简单了。

过程如下:


 

Cpp代码  收藏代码
  1. /** 
  2.     *最长递增子序列 
  3.     * 
  4.     */  
  5. #include <iostream>  
  6. #include <cstring>  
  7. using namespace std;  
  8.   
  9. //利用动态规划法O(n^2)  
  10. void longestIncreaseSub(int* a, int length, int* sub){  
  11.     int n = length;  
  12.     //利用一个辅助数列,记录子问题的值  
  13.     int *t = new int[n];//需要将t所有都初始化1,t记录子序列(子问题)的最长递增子序列长度  
  14.     int *p = new int[n];  
  15.     memset(p,0,sizeof(int));  
  16.     //初始的最大子序列长度默认为1,即自身组成一个最长递增子序列  
  17.     t[0]=1;  
  18.     for(int i=1; i<n; i++){  
  19.         t[i]=1;  
  20.         for(int j=0; j<i; j++){  
  21.             //这里面就是动态优化的状态转移方程  
  22.             if(a[j]<a[i] && t[j]>t[i]-1){  
  23.                 //里面存的是(0到i)的子序列中最长递增子序列的长度  
  24.                 t[i]=t[j]+1;  
  25.                 //符合要求的子序列位置(就是上一个子序列中的最后一个值的位置)  
  26.                 p[i]=j;  
  27.             }  
  28.         }  
  29.     }  
  30.     int m=0,k=0;  
  31.     for (int i=1;i<=n;i++)    
  32.   {  
  33.      if (m<t[i])  
  34.      {//m存t中最大值(就是要找的最长递增子序列),i是其所在的索引  
  35.        m = t[i];    
  36.        k = i;    
  37.      }  
  38.   }  
  39.   while(m>=0){  
  40.     sub[m-1] = a[k];  
  41.     m--;  
  42.     //p[k]中存着子序列中下一个值的下标位置  
  43.     k = p[k];  
  44.   }  
  45.       
  46.     for(int d=0; d<n; d++) cout<<t[d]<<" ";  
  47.     cout<<endl;  
  48.     for(int d=0; d<n; d++) cout<<p[d]<<" ";  
  49.     delete[] t;  
  50.     delete[] p;  
  51.     p = NULL;  
  52.     t = NULL;  
  53. }  
  54.   
  55. int main(){  
  56.     int a[] = { 1, 9, 3, 8, 11, 4, 5, 6, 4, 19, 7, 1, 7 };    
  57.     int length = sizeof(a)/sizeof(int);  
  58.     int* sub = new int[length];  
  59.     memset(sub,0,sizeof(int));//初始化sub  
  60.       
  61.     longestIncreaseSub(a,length, sub);  
  62.       
  63.     cout<<endl;  
  64.     for(int k=0; k<length; k++) cout<<sub[k]<<" ";  
  65.     delete[] sub;  
  66.     sub = NULL;  
  67. }  
 

 

 

 

7.上面的1,2,4,5的代码全部

 

Cpp代码  收藏代码
  1. /** 
  2.     *2.17 
  3.     *求最小子序列和,最小正子序列和,最大子序列乘积 
  4.     * 
  5.     */    
  6. #include <iostream>  
  7. #include <algorithm>    
  8. using namespace std;  
  9. #define INT_MIN -32768  
  10. #define INT_MAX 32767  
  11. //在里面有使用#define和const定义常量的比较,推荐使用const  
  12. //#define LEN 10  
  13. const int LEN = 10;  
  14.   
  15. /** 
  16.     *最大子序列和 
  17.     * 
  18.     */  
  19. int subMaxSum(int *a, int length){  
  20.     int thismax = 0, max=INT_MIN;  
  21.     for(int i=0; i<length; i++){  
  22.         thismax += a[i];  
  23.         if(thismax > max){  
  24.             max = thismax;  
  25.         }else if(thismax < 0){  
  26.             thismax = 0;  
  27.         }  
  28.     }  
  29.     return max;  
  30. }  
  31.   
  32. /** 
  33.     *最小子序列和 
  34.     *n 
  35.     */  
  36. int subMinSum(int a[], int length){  
  37.     int thismin = 0, min = INT_MAX;  
  38.     for(int i=0; i<length; i++){  
  39.         thismin += a[i];  
  40.         if(thismin < min){  
  41.             min = thismin;  
  42.         }else if(thismin > 0){  
  43.             thismin = 0;  
  44.         }  
  45.     }  
  46.     return min;  
  47. }  
  48.   
  49. struct Node    
  50. {    
  51.     int sum;    
  52.     int xiabiao;    
  53.         
  54. };    
  55. int cmp(const Node& t1,const Node& t2)    
  56. {    
  57.     return t1.sum < t2.sum;  
  58. }   
  59.   
  60. /** 
  61.     *最小正子序列和 
  62.     *n*logn 
  63.     */  
  64. int positiveSubMinSum(int *data, int len){  
  65.     Node* temp = new Node[len];    
  66.   temp[0].sum = data[0];    
  67.   temp[0].xiabiao = 0;    
  68.   for(int i=1;i<len;i++)    
  69.   {    
  70.     temp[i].sum = temp[i-1].sum+data[i];    
  71.     temp[i].xiabiao = i;    
  72.   }    
  73.   //对temp.sum[]进行从小到大排序,sum[]中只有相邻的两个数才有可能 得到 最小正子序列和    
  74.   sort(temp,temp+len,cmp);    
  75.   int sum = INT_MAX;    
  76.   for(int i=0;i<len-1;i++)    
  77.   {    
  78.     if(temp[i].xiabiao < temp[i+1].xiabiao)    
  79.     {    
  80.          if(temp[i+1].sum - temp[i].sum > 0 && temp[i+1].sum - temp[i].sum < sum)    
  81.          sum = temp[i+1].sum - temp[i].sum;    
  82.     }    
  83.   }    
  84.   delete temp;    
  85.   temp=0;    
  86.   return sum;    
  87. }  
  88.   
  89. void swap(int& a, int& b){  
  90.      int temp = a;  
  91.      a = b;  
  92.      b = temp;  
  93. }  
  94.   
  95. /** 
  96.     *最大子序列乘积(同时也求出了最小的子序列乘积) 
  97.     *在找最大的值得时候,必须记录最小值,因为有负数存在,最小的数可能变成最大的数 
  98.     */  
  99. int mutiSubMax(int *a, int length){  
  100.       int i;  
  101.     int maxProduct = 1;  
  102.     int minProduct = 1;  
  103.     int maxCurrent = 1;  
  104.     int minCurrent = 1;  
  105.   
  106.     for( i=0; i<length ;i++)  
  107.     {  
  108.         maxCurrent *= a[i];  
  109.         minCurrent *= a[i];  
  110.         if(maxCurrent > maxProduct)   
  111.             maxProduct = maxCurrent;  
  112.         if(minCurrent > maxProduct)  
  113.             maxProduct = minCurrent;  
  114.         if(maxCurrent < minProduct)  
  115.             minProduct = maxCurrent;  
  116.         if(minCurrent < minProduct)  
  117.             minProduct = minCurrent;  
  118.         //注意交换  
  119.         if(minCurrent > maxCurrent)  
  120.             swap(maxCurrent,minCurrent);  
  121.         //这个必须在最后(防止为0的时候)  
  122.         if(maxCurrent<1)  
  123.             maxCurrent = 1;  
  124.         //if(minCurrent>1)//这里不需要,因为通过交换即可,只需要一个  
  125.         //  minCurrent =1;  
  126.     }  
  127.     return maxProduct;  
  128. }  
  129.   
  130. int main(){  
  131.     int a[LEN] = {4,-1,5,-2,-1,2,6,-2,1,-3};  
  132.     cout<<"列表:"<<endl;  
  133.     for(int i=0; i<10; i++){  
  134.         cout<<a[i]<<" ";  
  135.     }  
  136.     cout<<endl;  
  137.     cout<<"最大子序列和:"<<subMaxSum(a, LEN)<<endl;  
  138.     cout<<"最小子序列和:"<<subMinSum(a, LEN)<<endl;  
  139.     cout<<"最小正子序列和:"<<positiveSubMinSum(a, LEN)<<endl;  
  140.     cout<<"最大子序列乘积:"<<mutiSubMax(a, LEN)<<endl;  
  141.     return 0;  
  142. }  

 

原创粉丝点击