分治演算法-最大子陣列問題
阿新 • • 發佈:2019-01-11
背景不做過多介紹,現在有這麼一個數組,裡面都是整數型(包含負數),求最大子陣列(連續幾個相加最大)。
首先我們分析問題,我們把此陣列看作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)
最後是我的java實現: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)
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;
}
}