分析与代码
只有当数组中包含负数时,最大子数组问题才有意义。如果所有数组元素都是非负的,那么显然这个数组本身就是最大子数组。
使用分治策略来求解:
想要用分治的思想来解决这个问题,显然需要我们对数组进行划分成规模相近的子数组。如果我们从数组的中间来划分,得到A[low]-A[mid]和A[mid+1]-A[high],
A[low]-A[high]的任何连续子数组A[i..j]与划分的数组必然有以下的关系:
1.完全位于子数组A[low]-A[mid]中
2.完全位于子数组A[mid+1]-A[high]中
3.跨越中点,即low<=i<=mid<j<=high i在mid左侧,j在mid右侧
所以这个数组的最大子数组的位置也应该有上述三种情况
对应到代码上,我们就要完成以下任务
1. 计算跨越中点的最大子数组
2. 计算中点左边最大子数组
3. 计算中点右边最大子数组
4. 比较三个最大子数组大小并返回其中最大的子数组的左右端点和大小
这其中的第四步就得到了一个分解后数组的最大子数组
#include<bits/stdc++.h>
using namespace std;
/*
计算跨越中点的最大子数组
*/
tuple<int,int,int> find_max_crossing_subArray(int A[],int low,int mid,int high){
int left_sum=-65536;
int right_sum=-65536;
int sum=0;
int max_left,max_right;
/*
以数组中点为起点向左遍历,left<-mid 的元素最大和
*/
for(int i=mid;i>=low;i--){
sum+=A[i];
if(sum>left_sum){
left_sum=sum;
max_left=i;
}
}
sum=0;
/*
和上面同理
*/
for(int i=mid+1;i<=high;i++){
sum+=A[i];
if(sum>right_sum){
right_sum=sum;
max_right=i;
}
}
return make_tuple(max_left,max_right,left_sum+right_sum);
}
tuple<int,int,int> find_maximum_subArray(int A[],int low, int high){
int left_low,
left_high,
left_sum,
right_low,
right_high,
right_sum,
cross_low,
cross_high,
cross_sum,
mid;
if(high==low){
return make_tuple(low,high,A[low]);
}
else {
mid=floor((low+high)/2);
//计算左侧最大子数组
tie(left_low,left_high,left_sum)=find_maximum_subArray(A,low,mid);
//计算右侧最大子数组
tie(right_low,right_high,right_sum)=find_maximum_subArray(A,mid+1,high);
//计算跨越中点的最大子数组
tie(cross_low,cross_high,cross_sum)=find_max_crossing_subArray(A,low,mid,high);
if(left_sum>=right_sum && left_sum>=cross_sum){
return make_tuple(left_low,left_high,left_sum);
}
else if(right_sum>=left_sum && right_sum>=cross_sum){
return make_tuple(right_low,right_high,right_sum);
}
else
return make_tuple(cross_low,cross_high,cross_sum);
}
}
int main(){
int A[]={2,3,6,8,-1,23,4342,-10000,32};
tuple B=find_maximum_subArray(A,0,8);
cout<<get<0>(B)<<" "<<get<1>(B)<<" "<<get<2>(B);
}
计算跨越中点的最大子数组起到合并的作用,比如说一个分解到后得到的长度为三的数组,对于这个数组,左侧最大子数组显然只有一个元素,右侧最大子数组也只有一个,(对应于代码中high==low的情况),然后计算跨越中点的最大子数组。
如果left_sum是最大的,那么原数组的对应侧的最大子数组元素就是left_sum,如果cross_sum是最大的,那么原数组对应侧的最大子数组就是cross_sum,以此类推。分治求解最大子数组问题其实就是将原数组分成多个子数组,并求出子数组中点的左侧最大子数组,右侧最大子数组,跨越中间的最大子数组,并进行比较得出这个子数组的最大子数组,然后这个子数组的最大子数组又作为上一层次数组的左、右或跨越mid的最大子数组,通过这样的递归就可以得到整个数组的最大子数组。
tuple的补充
元组是一个能容纳元素集合的对象。每个元素可以是不同的类型。使用tuple要使用以下头文件
#include<tuple>
常用的方法
tuple<int,int> A; //创建一个包含两个int类型元素的元组
tuple<int,float> A=make_tuple(1,1.1); //使用make_tuple可以创建一个tuple类型变量
get<1>(A); //对应A中第二个元素1.1
get<0>(A); //对应A中第一个元素1
int a,b;
tie(a,b)=make_tuple(3,4); //tie可以将参数与元组的元素一一对应