例如我们在求解下面的最大子数组时
13 | -3 | -25 | 20 | -3 | -16 | -23 | 18 | 20 | -7 | 12 | -5 | -22 | 15 | -4 | 7 |
最简单的做法就是暴力求解,但是时间复杂度为O(n^2);
//
// main.cpp
// 算法导论
//
// Created by SJCHEN on 2019/1/19.
// Copyright © 2019 SJCHEN. All rights reserved.
//
#include <iostream>
using namespace std;
#define INF 10000000
int A[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int len = sizeof(A)/sizeof(A[0]);
int max = -INF;
int FindMaxSubarray(int A[])
{
int max = -INF;
int sum = 0;
for (int left = 0; left < len; left++) {
sum = 0;
for (int right = left; right < len; right++) {
sum += A[right];
if (sum > max) {
max = sum;
}
}
}
return max;
}
int main()
{
cout << FindMaxSubarray(A) << endl;
return 0;
}
那么有没有更好的做法呢?
答案当然是有,我们可以使用分治策略的求解方法。
使用分治技术意味着我们要将子数组划分为两个规模尽量相等的子数组。
当然我们可以发现,在A[low, high]中的任何连续子数组A[i,j]所处的位置必然是以下三种情况之一:
一,完全位于子数组A[low, mid]中,low <= i <= j <= mid
二,完全位于子数组A[mid + 1, high]中,mid < i<= j <=high
三,跨越了中点,因此low <= i <=mid <= j <= high
//
// main.cpp
// 算法导论
//
// Created by SJCHEN on 2019/1/19.
// Copyright © 2019 SJCHEN. All rights reserved.
//
#include <iostream>
using namespace std;
#define INF 10000000
int A[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int len = sizeof(A)/sizeof(A[0]);
int MaxLeft = 0, MaxRight = 0;
int leftsum = -INF, rightsum = -INF, crosssum = -INF;
int leftlow = -INF, lefthigh = -INF, rightlow = -INF, righthigh = - INF;
int crosslow = - INF, crosshigh = -INF;
int FindMaxCrossingSubarray(int low, int mid, int high)
{
leftsum = -INF;
int sum = 0;
for (int i = mid; i >= low; i--) {
sum += A[i];
if (sum > leftsum) {
leftsum = sum;
MaxLeft = i;
}
}
rightsum = -INF;
sum = 0;
for (int j = mid + 1; j <= high; j++) {
sum += A[j];
if (sum > rightsum) {
rightsum = sum;
MaxRight = j;
}
}
return (leftsum + rightsum);
}
int FindMaxIMUMSubarray(int low, int high)
{
if (high == low)
return A[low];
else {
int mid = (low + high) / 2;
leftsum = FindMaxIMUMSubarray(low, mid);//递归求解左边
rightsum = FindMaxIMUMSubarray(mid + 1, high);//递归求解右边
crosssum = FindMaxCrossingSubarray(low, mid, high);//横跨左右
if (leftsum >= rightsum && leftsum >= crosssum)//左
return leftsum;
else if (rightsum >= leftsum && rightsum >= crosssum)//右
return rightsum;
else return crosssum;//两边
}
}
int main()
{
cout << FindMaxIMUMSubarray(0, len - 1) << endl;
return 0;
}