最大子数组问题求解
将数组A分成两部分,A[left...mid]和A[mid+1..right]两部分,求解最大子数组之和包含了三种可能的情况:
1.完全位于子数组A[left...mid]中,因此low<=i<=j<=mid;
2.完全位于子数组A[lmid+1...high]中,因此mid+1<=i<=j<=high;
3.跨越了中点,因此ow<=i<mid<j<=high;
如图所示
对于上述的三种情况,1和2可以利用递归方法求解最大子数组问题,因为这两个问题仍然是最大子数组问题,只是规模更小;对于3既是求解两个最大子数组之和的情况,因为跨越中点的数组既是由两个子数组A[i...mid]和A[mid+1...j]组成,因此分别求解,然后将其合并。
代码如下
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;
/*
求解数组的最大子数组和的问题分为三种情况
1、最大子数组位于Array[low...mid]之间
2、最大子数组位于Array[mid+1...high]之间
3、最大子数组位跨越了数组的中点,因此最大子数组为Array[i...j],其中low<=i<=mid<j<=high
*/
typedef struct result
{
int left;//左索引
int right;
int sum;//求和
}*Res;
//由于要得到两个坐标和一个和,故而我们使用结构体
//求解跨越了中点的最大子数组,任何跨越了中点的子数组都是由两部分组成Array[i..mid]和Array[mid+1,j]
Res Find_Max_Crossing_SubArray(int* arr,int low,int mid,int high)
{
Res R = (Res)malloc(sizeof(struct result));
int left_sum(arr[mid]),right_sum(arr[mid+1]),sum(0);//其中sum保存所有元素的和,left_sum保存目前为止找到的最大值的和
int i(0),j(0);
int max_left(mid),max_right(mid+1);
for (i = mid;i>=low;i--)
{
sum = sum + arr[i];//保存从low到mid的所有值得和
if (sum > left_sum)
{
left_sum = sum;
max_left = i;//记录目前找到的最大和的下标
}
}
sum = 0;
for (j = mid + 1;j <= high;j++)
{
sum = sum + arr[j];
if (sum > right_sum)
{
right_sum = sum;
max_right = j;//记录目前找到的最大和的下标
}
}
R->left = max_left;
R->right = max_right;
R-> sum = (left_sum + right_sum);
return R;
}
//求解另外两种情况的最大子数组之和,可以使用分治技术,
Res Find_Maximum_SubArray(int* arr,int low,int high)
{
if(high == low)
{
Res res = (Res)malloc(sizeof(result));
res->left = low;
res->right = high;
res->sum = arr[low];
return res;//只有一个元素
}
else
{
int mid = (low + high)/2;
Res resLeft = Find_Maximum_SubArray(arr,low,mid);
Res resRight = Find_Maximum_SubArray(arr,mid + 1,high);
Res resCross = Find_Max_Crossing_SubArray(arr,low,mid,high);
if(resLeft->sum >= resRight->sum && resLeft->sum >= resCross->sum)
return resLeft;
else if(resRight->sum >= resLeft->sum && resRight->sum >= resCross->sum)
return resRight;
else
return resCross;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int array[16]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
//int left,right,sum;
Res res = Find_Maximum_SubArray(array,0,16);
//printf("left = %d,right = %d,sum = %d\n",left,right,sum);
cout <<"left = "<<res->left<<endl;
cout<<"right = "<<res->right<<endl;
cout<<"sum = "<<res->sum<<endl;
system("pause");
return 0;
}
给出对比算法
//算法1----暴力法(O(n^3))
int MaxSubseqSum1(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j, k;
for (i = 0; i < N; i++) { //i是子列左端位置
for (j = i; j < N; j++) { //j是子列右端位置
ThisSum = 0; //ThisSum是A[i]到A[j]的子列和
for (k = 0; k < N; k++)
ThisSum += A[k]; //
if (ThisSum > MaxSum) //如果刚得到的这个子列和更大,则更新
MaxSum = ThisSum;
}//j循环结束
} //i循环结束
return MaxSum;
}
//算法2-----O(n^2)
int MaxSubseqSum2(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j;
for (i = 0; i < N; i++)//i是子列左端位置
{
ThisSum = 0; //ThisSum是A[i]到A[j]的子列和
for (j = i; j < N; j++) //j是子列右端位置
{
ThisSum += A[j];
//对于相同的i不同的j只要在j-1次循环的基础上累加1项即可
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
//在线处理O(n)
int MaxSubseqSum4(int A[], int N)
{
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for (i = 0; i < N; i++)
{
ThisSum += A[i];//向右累加
if (ThisSum > MaxSum)
MaxSum = ThisSum;
else if (ThisSum < 0)//如果当前子列和为负数,则抛弃
ThisSum = 0;
}
return MaxSum;
}