明日复明日,明日何其多。我生待明日,万事成蹉跎。
求解最大子数组的问题一般会有类似这样的描述:给定一个数组,求出数组中一个连续的子数组,该子数组中元素的和为所有连续子数组中的最大值。
这样的问题是典型分治思想,适合使用递归处理。我们可以考虑将给定的数组分成两部分,那么最大子数组必定存在于左侧部分,或者右侧部分,或者是跨越了分界线的部分。那么,只要分别求出这三种情况下的最大子数组,再进行比较,就能得出最后的结果。从这里的分析可以看出,我们采用了分解数组(分治思想),继而对每次的分解结果进行同样的处理(递归),最后求解(合并)。
C++实现示例:
int main()
{
int A[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};
int low = 0;
int high = 0;
int sum = 0;
find_max_subarray(A, 0, 15, low, high, sum);
cout << "{";
for (int i = low; i <= high; i++)
{
cout << A[i] << ", ";
}
cout << "(sum = " << sum << ")" << "}" << endl;
system("pause");
return 0;
}
void find_max_subarray(int* arr, int low, int high,
int& result_low, int& result_high, int& result_sum)
{
if (low == high)
{
result_low = low;
result_high = high;
result_sum = arr[low];
return;
}
int mid = (low + high) / 2;
int left_low = 0;
int left_high = 0;
int left_sum = 0;
find_max_subarray(arr, low, mid, left_low, left_high, left_sum);
int right_low = 0;
int right_high = 0;
int right_sum = 0;
find_max_subarray(arr, mid + 1, high, right_low, right_high, right_sum);
int cross_low = 0;
int cross_high = 0;
int cross_sum = 0;
find_max_crossing_subarray(arr, low, mid, high, cross_low, cross_high, cross_sum);
if (left_sum >= right_sum && left_sum >= cross_sum)
{
result_low = left_low;
result_high = left_high;
result_sum = left_sum;
}
else if (right_sum >= left_sum && right_sum >= cross_sum)
{
result_low = right_low;
result_high = right_high;
result_sum = right_sum;
}
else
{
result_low = cross_low;
result_high = cross_high;
result_sum = cross_sum;
}
}
void find_max_crossing_subarray(int* arr, int low, int mid, int high,
int& cross_low, int& cross_high, int& cross_sum)
{
int sum = 0;
int left_sum = arr[mid] - 1;
for (int i = mid; i >= low; i--)
{
sum += arr[i];
if (sum >= left_sum)
{
left_sum = sum;
cross_low = i;
}
}
sum = 0;
int right_sum = arr[mid + 1] - 1;
for (int i = mid + 1; i <= high; i++)
{
sum += arr[i];
if (sum >= right_sum)
{
right_sum = sum;
cross_high = i;
}
}
cross_sum = left_sum + right_sum;
}
Java实现示例:
public static void main(String[] args) {
int A[] = { 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7 };
int[] result = new int[3];
findMaxSubarray(A, 0, 15, result);
// output
for (int i = result[0]; i <= result[1]; i++) {
System.out.print(A[i]);
System.out.print(", ");
}
System.out.println("Sum = " + result[2]);
}
public static void findMaxSubarray(int[] arr, int low, int high, int[] result) {
if (low == high) {
result[0] = low; // the left index of the array
result[1] = high; // the right index of the array
result[2] = arr[low]; // the sum result of the sub-array
return;
}
int mid = (low + high) / 2;
// index 0 - low index of the sub-array
// index 1 - high index of the sub-array
// index 2 - sum of the sub-array
int[] leftResult = new int[3];
findMaxSubarray(arr, low, mid, leftResult);
int[] rightResult = new int[3];
findMaxSubarray(arr, mid + 1, high, rightResult);
int[] crossResult = new int[3];
findMaxCrossingSubarray(arr, low, mid, high, crossResult);
if (leftResult[2] >= rightResult[2] && leftResult[2] >= crossResult[2]) {
System.arraycopy(leftResult, 0, result, 0, 3);
}
else if (rightResult[2] >= leftResult[2] && rightResult[2] >= crossResult[2]) {
System.arraycopy(rightResult, 0, result, 0, 3);
}
else {
System.arraycopy(crossResult, 0, result, 0, 3);
}
}
public static void findMaxCrossingSubarray(int[] arr, int low, int mid, int high, int[] result) {
int sum = 0;
int left_sum = arr[mid] - 1;
for (int i = mid; i >= low; i--) {
sum += arr[i];
if (sum > left_sum) {
left_sum = sum;
result[0] = i; // low index of the sub-array
}
}
sum = 0;
int right_sum = arr[mid + 1] - 1;
for (int i = mid + 1; i <= high; i++) {
sum += arr[i];
if (sum > right_sum) {
right_sum = sum;
result[1] = i; // high index of the sub-array
}
}
result[2] = left_sum + right_sum;
}