背景不做过多介绍,现在有这么一个数组,里面都是整数型(包含负数),求最大子数组(连续几个相加最大)。
首先我们分析问题,我们把此数组看作A[low..high],我们将要用分治法求出其最大的子数组。使用分治法意味着我们要将数组划分为两个规模尽量相等的子数组(这里用尽量因为有时候是奇数个,无法均分),找到数组的中央位置,比如mid,然后考虑求解两个子数组A[low..mid]和A[mid+1..high]。那么子数组A[i..j]所有的情况都逃脱不了一下三种:
- 完全位于子数组A[low..mid]中,low<=i<=j<=mid
- 完全位于子数组A[mid+1..high]中,mid<i<=j<=high
- 跨越了中点,因此low<=i<=mid<j<=high
FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)
left_sum=-∞
sum=0
for i=mid downto low
sum=sum+A[i]
if sum>left_sum
left_sum=sum
max_left=i
right_sum=-∞
sum=0
for j=mid+1 to high
sum=sum+A[j]
if sum>right_sum
right_sum=sum
max_right=j
return (max_left,max_right,left_sum+right_sum)
然后是主的递归伪代码:
FIND-MAXIMUM-SUBARRAY(A,low,high)
if high==low
return (low,high,A[low])
else
mid= ⌊(low+high)/2⌋
(left_low,left_high,left_sum)=FIND-MAXIMUM-SUBARRAY(A,low,mid)
(right_low,right_high,right_sum)=FIND-MAXIMUM-SUBARRAY(A,mid+1,high)
(cross_low,cross_high,cross_sum)=FIND-MAXIMUM-SUBARRAY(A,low,mid,high)
if left_sum>=right_sum and left_sum>=cross_sum
return (left_low,left_high,left_sum)
elseif right_sum>=left_sum and right_sum>=cross_sum
return (right_low,right_high,right_sum)
else
return (cross_low,cross_high,cross_sum)
最后是我的java实现:
package com.test;
import java.util.ArrayList;
import java.util.List;
public class MyTest02 {
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[] s = getMaxSummary(a, 0, 15);
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
}
/**
* 程序主入口
* @param A
* @param low
* @param high
* @return
*/
public static int[] getMaxSummary(int[] A, int low, int high) {
if (low == high) { // 如果長度就一個,那麼就把這個取出來
int[] result = { low, high, A[low] };
return result;
} else {
int middle = (int) Math.floor((low + high) / 2); // 获取中间值
int[] left = new int[3]; // 保存左边部分返回结果
int[] right = new int[3]; // 保存右边部分返回结果
int[] cross = new int[3]; // 返回交叉部分返回结果
left = getMaxSummary(A, low, middle);
right = getMaxSummary(A, middle + 1, high);
cross = getMaxCrossMid(A, low, high, middle);
if (left[2] >= right[2] && left[2] >= cross[2]) { // 那部分大就用了那部分
return left;
} else if (right[2] >= left[2] && right[2] >= cross[2]) {
return right;
} else {
return cross;
}
}
}
/**
* 獲取最大子數組(一部分在左邊,一部分在右邊)
*
* @param A
* @param low
* @param high
* @param middle
* @return
*/
public static int[] getMaxCrossMid(int[] A, int low, int high, int middle) {
int leftSum = Integer.MIN_VALUE;
int sum = 0; // 保存和的
int left = 0; // 记录左边位置
for (int i = middle; i >= low; i--) {
sum = sum + A[i];
if (sum > leftSum) { // 证明所加数字为正数,那么符合条件(因为最大子数组内正数越多指定越大)
leftSum = sum;
left = i;
}
}
int rightSum = Integer.MIN_VALUE;
int sum2 = 0;
int right = 0; // 记录右边位置
for (int i = middle + 1; i <= high; i++) {
sum2 = sum2 + A[i];
if (sum2 > rightSum) {
rightSum = sum2;
right = i;
}
}
int[] result = new int[3];
result[0] = left;
result[1] = right;
result[2] = leftSum + rightSum;
return result;
}
}