求数组的最大子数组

本文详细介绍了寻找数组最大和子数组的多种算法,包括暴力求解、递归(分治法)、线性时间内求解及优化算法,并通过实例展示了如何使用自定义的SubArray类型进行动态规划求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

求数组的最大和子数组是经典问题,许多书籍都提到,比如算法导论,编程珠玑。更是覆盖很多面试书,比如编程之美,剑指Offer等。以下是我关于该问题的理解与实现。

  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<climits>
  4 using namespace std;
  5  
  6 class SubArray{
  7 private: 
  8     int left;
  9     int right;
 10     int sum; 
 11 public:
 12     void set(int l,int r,int s){
 13     
 14         this->left = l;
 15         this->right = r;
 16         this->sum = s; 
 17     } 
 18     void write(){
 19         
 20         cout<<this->left<<" ";
 21         cout<<this->right<<" ";
 22         cout<<this->sum<<endl;
 23     }
 24     void setLeft(int l){
 25         
 26         this->left = l;
 27     }
 28     void setRight(int r){
 29         
 30         this->right = r;
 31     }
 32     void setSum(int s){
 33         
 34         this->sum = s;
 35     }
 36     int getSum(){
 37         
 38         return this->sum;
 39     }
 40     int getLeft(){
 41         
 42         return this->left; 
 43     }
 44     int getright(){
 45         
 46         return this->right; 
 47     } 
 48     void equal(SubArray sa){
 49         
 50         this->set(sa.getLeft(),sa.getright(),sa.getSum());
 51     } 
 52  };
 53  
 54  
 55 SubArray max(SubArray a,SubArray b){
 56     return a.getSum() > b.getSum() ? a : b; 
 57 } 
 58 
 59 
 60 
 61 //1、暴力求解最大子数组问题 
 62  SubArray FindMaximumSubArray1(int a[],int low,int high){
 63      
 64      int sum = INT_MIN;
 65      SubArray sa;
 66      sa.set(low,low,sum);                 //初始化子数组 
 67      for(int i=low;i<=high-1;++i){
 68          
 69         int temp = 0;
 70          for(int j=i;j<=high;++j){
 71               
 72              temp += a[j];
 73      
 74              if(sa.getSum()<temp){
 75                  
 76                  sa.setSum(temp);
 77                  sa.setLeft(i); 
 78                  sa.setRight(j);     
 79              }
 80             
 81          }
 82              
 83      }
 84      return sa; 
 85  }
 86  //2、递归(分治法) 
 87  //2.1
 88  //求得跨中点的最大子数组 
 89  SubArray FindMaxCrossingSubArray(int a[],int low,int mid,int high){
 90      
 91      int LeftSum = INT_MIN;
 92      int RightSum = INT_MIN; 
 93      int sum = 0;
 94      int Left = 0;
 95      int Right = 0;
 96      SubArray sa;
 97      for(int i=mid;i>=low;--i){
 98          
 99          sum += a[i];
100          if(LeftSum < sum){
101              
102              LeftSum = sum;
103              Left = i;
104          }
105      }
106      sum = 0;
107      for(int i=mid+1;i<=high;++i){
108          
109          sum += a[i];
110          if(RightSum < sum){
111              
112              RightSum = sum;
113              Right = i;
114          }
115      }
116      sa.set(Left,Right,LeftSum+RightSum);
117      return sa;
118      
119  } 
120  SubArray FindMaximumSubArray2(int a[],int low,int high){
121      
122      SubArray sa,Lsa,Rsa;
123      int mid; 
124      if(low == high){
125         
126         sa.set(low,high,a[low]);
127          return sa;
128      }else{
129          
130          mid = (low+high)/2;
131          
132          Lsa = FindMaximumSubArray2(a,low,mid);
133          Rsa = FindMaximumSubArray2(a,mid+1,high);
134         sa  = FindMaxCrossingSubArray(a,low,mid,high);
135         
136         if(Lsa.getSum()>Rsa.getSum() && Lsa.getSum()>sa.getSum())
137             return Lsa;
138         else if(Lsa.getSum()<Rsa.getSum() && sa.getSum()<Rsa.getSum()) 
139              return Rsa;
140         else
141             return sa;
142          
143      }
144 
145  } 
146  //线性时间内求得最大子数组 (动态规划)
147  SubArray FindMaximumSubArray3(int a[],int low,int high) {
148      /*以下来自编程之美(从后向前分析) 
149      从分治法的到提示:
150      考虑a[low]与a[low]~a[high]的最大和子数组(假设为a[i]~a[j])间的三种关系:
151     1、i=j=low,即a[low]本身就是最大和子数组。
152     2、i=low,j>i,即最大和子数组包含a[low]。
153     3、j>i>low,即最大和子数组与a[low]无关系
154     那么我们可以减问题规模缩减 1 。
155     假设all[1ow+1]表示a[low+1]~a[high]的最大和子数组的和且为已知,所以all[low]即为所求的最大和子数组的和 
156     start[low+1]表示以a[low+1]开始的a[low+1]~a[high]的最大和子数组的和
157     all[high] 就是 a[high] 
158     start[high]就是 a[high] 
159     因此all[low]=max{a[low],a[low]+start[low+1],all[low+1]} 因此用动态规划解决 
160     */ 
161     /*这里将同时获得子数组位置,因此用上面的自定义的"SubArray"类型进行改写 
162     假设sall[i]表示a[i]~a[high]的最大和字数组,所以sall[low]即为所求
163     sstart[i]表示以a[i]开始的a[i]~a[high]的最大和子数组 
164     因此sall[high] sstart[high]都为(high,high,a[high]) 
165     */ 
166     int length = high-low+1;
167     int temp;
168     SubArray *sall,*sstart,sa,rsa; 
169      sall = (SubArray *)malloc(sizeof(SubArray)*(length));
170      sstart = (SubArray *)malloc(sizeof(SubArray)*(length));
171      sall[high].set(high,high,a[high]);
172      sstart[high].set(high,high,a[high]);     
173      for(int i=high-1;i>=low;--i){
174      
175          sa.set(i,i,a[i]);
176          rsa.set(i,sstart[i+1].getright(),sstart[i+1].getSum()+a[i]);
177         //已知sstart[i+1],求得sstart[i]
178         sstart[i] = max(sa,rsa);
179         //已知sstart[i]和sall[i+1]求得sall[i] 
180         sall[i] = max(sall[i+1],sstart[i]);
181     }
182      return sall[low]; 
183      
184 
185  }
186  SubArray FindMaximumSubArray4(int a[],int low,int high){
187      
188      /*观察算法,我们利用了以下递推式:
189     sstart[i] = max(sa,rsa);       
190     sall[i] = max(sall[i+1],sstart[i]);    
191     如果sstart[i].getSum() < 0, sstart[i]=sa(a[i]),这就说明如果某个i使得是sstart[i].getSum()小于0,就要以当前位置i的a[i]计算sstart[i] 
192     而且算法3增加了空间复杂度,可以观察到俩个子数组sall[],sstart[]只有sall[i+1]和sstart[i+i]有用,所以可以变换算法为4和5: 
193      */
194     SubArray sall,sstart,sa,rsa; 
195      sall.set(high,high,a[high]);
196      sstart.set(high,high,a[high]);     
197      for(int i=high-1;i>=low;--i){
198          sa.set(i,i,a[i]);
199          rsa.set(i,sstart.getright(),sstart.getSum()+a[i]);
200         //已知sstart,求得sstart
201         sstart = max(sa,rsa);
202         //已知sstart和旧的sall求得新的sall 
203         sall = max(sall,sstart);
204      }
205      return sall; 
206      
207  }
208  
209 
210  SubArray FindMaximumSubArray5(int a[],int low,int high){
211      
212      
213     SubArray sall,sstart,sa,rsa; 
214      sall.set(high,high,a[high]);
215      sstart.set(high,high,a[high]);     
216      for(int i=high-1;i>=low;--i){
217          
218         sa.set(i,i,a[i]);
219          if(sstart.getSum()<0)
220              
221              sstart.equal(sa);
222          else
223              sstart.set(i,sstart.getright(),sstart.getSum()+a[i]);
224          
225          if(sstart.getSum()>sall.getSum())
226              sall.equal(sstart);
227      }
228      return sall; 
229      
230  }
231  
232  int main(){
233      int num;
234      SubArray sa;
235      while(1){    
236          cin>>num;
237          int *array=NULL;
238          array = (int *)malloc(sizeof(int)*(num));
239          for(int i=0;i<num;i++)    cin>>array[i]; 
240          sa=FindMaximumSubArray5(array,0,num-1);
241          sa.write();
242         // for(int i=0;i<num;i++)    cout<<array[i]<<" ";
243      }
244      return 0;
245  }

 参考:

1、算法导论

2、编程之美

3、博客:http://www.ahathinking.com/archives/120.html

4、博客:http://blog.youkuaiyun.com/v_JULY_v/article/details/6444021

 

转载于:https://www.cnblogs.com/goongqk/p/FindMaximumSubArray.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值