编程之美 ——2.14 求数组的子数组之和的最大值

/**

 * 一个有N个整数元素的一位数组(A[0], … A[N – 1]), 这个数组有很多子数组, 求子数组之和的最大值.

 */

#include <stdio.h>

struct SubArraySum {

int begin;

int end;

int sum;

};

/**

 * 将数字分为两种, 小于0的和大于等于0的, 所以数组number应该如下所示:

 * ……负, 负, 非负, 非负, 非负, 非负, 负, 负, 负, 负, 非负, 非负, 非负……, 如此往复

 * 将每段连续存在的负数和非负数的序列相加, 再构成一个数列, 从而得到一个所示的负数和非负数相间分布的数列:

 * ……负, 非负, 负, 非负, 负, 非负……

 * 现在使用struct SubArraySum类型变量max记录和最大的子数组的起始下标begin和end, 以及和sum

 * 然后比较相邻的一个负数minus和非负数plus, minus和plus相邻, 并且起始下标为begin, 结束下标为i – 1(使用i遍历整个数组, i – 1为plus中最后一个非负数的下标), 同时mid为plus中第一个非负数的下标

 * 最后定义temp为在max和minus之间, 且与minus所在区间相邻的子数组之和最大的子数组值. 当max与minus相邻时, temp不存在, temp与max同类型.

 * 然后便可分为两种情况讨论:

 * max所在区间与minus和plus的区间相邻, 即begin = max.end + 1:

 ***** 此时再进行判断, minus + plus的符号

 ***** minus + plus >= 0, 此时max.sum + minus + plus > max.sum, 作如下讨论:

 ********** max.sum >= 0时,可以将两个区间合在一起, 从而得到一个新的区间, 此区间为子数组之和最大的区间, max.sum = plus + minus + max.sum; max.end = i – 1;

 ********** max.sum < 0时, 不可以将两个区间合在一起, plus即为此时的最大区间, 所以 max.sum = plus; max.begin = mid; max.end= i – 1;

 ***** minus + plus < 0, 此时max.sum + minus + plus < max.sum, 此时需要比较max.sum和plus, 作如下讨论:

 ********** max.sum <= plus时, 取plus的区间代替原来max的区间, max.beign = mid, max.end = i – 1, max.sum = plus

 ********** max.sum > plus时, 保留下max, 同时令temp = plus, max区间与接下来需要讨论的区间不再相邻, 需要用到temp的值. 由于plus为max后面唯一的一个正数区间, 所以temp.sum = plus, temp.begin = mid, temp.end = i – 1即可

 * begin != max.end + 1, max所在区间与minus和plus的区间不相邻, 此时需要比较max与temp + minus + plus的值, 首先可以确定temp < max.sum

 ***** plus + minus >= 0时, temp.sum + plus + minus存在大于max.sum的可能

 ********** temp.sum + plus + minus >= max.sum, max区域改变,

 *************** 如果temp.sum + plus <= 0, plus则成了最大的区域, 将max改变为plus的区域, max.begin = mid, max.end = i – 1, max.sum = plus, continue

 *************** 如果temp.sum + plus > 0, temp.sum + minus + plus为最大区域,  max.begin = temp.begin, max.end = i – 1, max.sum = temp.sum + plus + minus, continue

 ********** plus >= max.sum, max区域改变, 变为plus的区域, max.begin = mid, max.end = i – 1, max.sum = plus, continue

 ********** plus < max.sum时, 区域保持不变, 但此时需要讨论temp的变化

 ***** plus + minus < 0时, 此时temp + plus + minus < max.sum, 所以只需要比较plus和max.sum的值即可

 ********** plus >= max.sum时, 区域改变, max.begin = mid, max.end = i – 1, max.sum = plus, continue

 ********** plus < max.sum时, max区域保持不变, 此时需要讨论temp的变化

 ***** 当max区域保持不变时, 需要讨论temp的变化, 因为temp.sum + minus + plus与temp.sum与plus中的最大值不确定.

 ***** 由于temp必须与minus相邻, 因此实际上是讨论plus是否需要加上temp和minus的区域, 即判断temp.sum + minus的正负

 ********** temp.sum + minus > 0, 新的temp为temp与minus和plus的总和的区域

 ********** temp.sum + minus <= 0, 新的temp为plus的区域

 */

struct SubArraySum GetSum(int number[], int  N) {

int plus, minus;
// 分别记录数组number中某段正数子数组和负数子数组的和

struct SubArraySum max;
// 记录number中子数组之和的最大值

struct SubArraySum temp;

int i = 0, begin, mid, index, MAX;

max.begin = -1;

max.end = -1;

max.sum = -1;

begin = 0;

index = 0;

MAX = number[index];

while(i < N) {

plus = 0;

minus = 0;

begin = i;

while (number[i] < 0 && i < N) {

if(MAX < number[i]) {

index = i;

MAX = number[i];

}

minus += number[i];

++i;

}

mid = i;
// 正元素的起始下标

while (number[i] >= 0 && i < N) {

if(MAX < number[i]) {

index = i;

MAX = number[i];

}

plus += number[i];

++i;

}

// 表明已经找出来的和最大的区间与当前处理的区间相连

if (begin == max.end + 1) {

if (plus + minus >= 0) {

if (max.sum >= 0) {

max.sum += plus + minus;

max.end = i – 1;

continue;

} else {

max.begin = mid;

max.end = i – 1;

max.sum = plus;

continue;

}

} else {

if (max.sum <= plus) {

max.begin = mid;

max.end = i – 1;

max.sum = plus;

continue;

} else {

temp.sum = plus;

temp.begin = mid;

temp.end = i – 1;

continue;

}

}

} else {

if (plus + minus >= 0) {

if (temp.sum + minus + plus >= max.sum) {

if (temp.sum + plus <= 0) {

max.begin = mid;

max.end = i – 1;

max.sum  = plus;

continue;

} else {

max.begin = temp.begin;

max.end = i – 1;

max.sum = temp.sum + plus + minus;

continue;

}

} else if (plus >= max.sum) {

max.begin = mid;

max.end = i – 1;

max.sum = plus;

continue;

}

} else {

if (plus >= max.sum) {

max.begin = mid;

max.end = i – 1;

max.sum = plus;

continue;

}

}

if (temp.sum + minus > 0) {

temp.sum += plus;

temp.end = i – 1;

}

else {

temp.sum = plus;

temp.begin = mid;

temp.end = i – 1;

}

}

}

if (MAX <= 0) {

max.begin = index;

max.end = index;

max.sum = MAX;

}

return max;

}

int main() {

//int number[] = {-2, 5, 3, -6, 4, -8, 6};

//int number[] = {-1, -1, -3, -4, -5, -6, -7, -8, -9, -10, 0};

int number[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

struct SubArraySum max = GetSum(number, sizeof(number) / sizeof(int));

printf(“数组子数组之和的最大值为%d, 其下标从%d至%d\n”, max.sum, max.begin, max.end);

return 0;

}

以上算法为自己想的算法, 时间复杂度为O(N), 若有错误, 请在留言中指出, 不胜感激.

    原文作者:mxk19930509
    原文地址: https://blog.csdn.net/u011387543/article/details/80817809
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞