编程之美 ——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), 若有错误, 请在留言中指出, 不胜感激.

猜你喜欢

转载自www.cnblogs.com/KingOfAlex/p/9230035.html