1. 程式人生 > >程式設計之美 ——2.14 求陣列的子陣列之和的最大值

程式設計之美 ——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), 若有錯誤, 請在留言中指出, 不勝感激.