求解一個數組的最大子數組,例如給定數組B[0,,,,,,n-1],就是找到滿足條件的一組i和j使得當i <= j時,有B[j] - B[i]的值達到最大。這裏我們很容易想到一種暴力求解的方法。從n個下標中任意選取兩個,就是n*(n-1)/2中組合,來計算得到最大子數組即可。此時這種算法的時間複雜度爲O(n^2),但是若我們採用如下分治策略來進行優化的話,我們可以得到一個算法時間複雜度爲O(nlogn)的算法。
分治策略:首先我們來對問題進行一下轉換,把求B的最大子數組的問題轉換成求A的最大子數組問題,數組A即反應B中數的變化的一個數組,其中一個元素A[i](i >=1)代表的是B[i]-B[i-1],這樣的話,我們看問題的角度就發生了一定的變化,即不在關心B中的每個數是多少,而是關心B中每個數相對於前一個數是如何變化的,即問題現在轉化成了尋找A的和最大的非空連續子數組。
在明確了問題轉化成爲求解A的和最大的非空連續子數組後,我們來思考如何用分治技術來求解最大子數組問題。假定我們尋找子數組A[low.......high]的最大子數組。使用分治技術意味着我們要將子數組劃分爲兩個規模儘量相等的子數組。也就是說,找到子數組的中央位置,比如mid,然後考慮求解兩個子數組A[low....mid]和A[mid+1...high]。經分析可知,A的任何連續子數組A[i....j]所處的位置必然是以下三種情況之一:
- 完全位於子數組A[low....mid]中,因此low <= i <= j <= mid
- 完全位於子數組A[mid+1....high]中,因此mid+1 <= i <= j <= high
- 跨越了中點,因此low <= i <= j <= high
- 若low == high 返回low,high,A[low]或者A[high]
- 計算mid = (low+high)/2
- 計算A(low,mid)的最大子數組
- 計算A(mid+1,high)的最大子數組
- 計算橫跨中點的A(low,mid,high)的最大子數組
- 選取一個最大的即可
#include<iostream>
#include<climits>
using namespace std;
//定義子數組結構體,分別代表子數組的下界和上界,以及和
struct sub_array {
int max_left;
int max_right;
int max_sum;
};
//尋找跨越中點的最大子數組的函數
sub_array Find_Max_Crossing_Subarray(int *A, int low, int mid, int high) {
int left_sum = INT_MIN;
int right_sum = INT_MIN;
int sum = 0;
sub_array cross_array;
//計算中點左側的最大子數組
for (int i = mid; i >= 1; i--) {
sum = sum + A[i];
if (sum > left_sum) {
left_sum = sum;
cross_array.max_left = i;
}
}
sum = 0;
//計算中點右側的最大子數組
for (int j = mid + 1; j <= high; j++) {
sum = sum + A[j];
if (sum > right_sum) {
right_sum = sum;
cross_array.max_right = j;
}
}
//將二者進行合併
cross_array.max_sum = left_sum + right_sum;
return cross_array;
}
//尋找最大子數組的函數
sub_array Find_Maximun_Subarray(int *A, int low, int high) {
int mid;
if (high == low) { //數組中只有一個元素的情況
sub_array temp;
temp.max_left = low;
temp.max_right = high;
temp.max_sum = A[low];
return temp;
}
else {
mid = (low + high) / 2;
sub_array cross_array;
sub_array left_array;
sub_array right_array;
left_array = Find_Maximun_Subarray(A, low, mid); //計算左子數組
right_array = Find_Maximun_Subarray(A, mid + 1, high); //計算右子數組
cross_array = Find_Max_Crossing_Subarray(A, low, mid, high); //計算橫跨中點的子數組
//尋找最大的情況
if (left_array.max_sum >= right_array.max_sum && left_array.max_sum >= cross_array.max_sum) {
return left_array;
}
else if (right_array.max_sum >= left_array.max_sum && right_array.max_sum >= cross_array.max_sum) {
return right_array;
}
else {
return cross_array;
}
}
}
int main() {
int num;
cin >> num;
int *B = new int[num];
int *A = new int[num];
for (int i = 0; i < num; i++) {
cin >> B[i];
}
for (int i = 1; i < num; i++) {
A[i] = B[i] - B[i-1];
}
sub_array solution;
solution = Find_Maximun_Subarray(A, 1, num-1);
cout << solution.max_left<<" "<< solution.max_right<<" "<<solution.max_sum << endl;
system("pause");
}