算法导论讲分治策略这一章提到了一个经典的问题,求一个数组的非空连续子数组的最大值,数组的元素可正、可负、可为零。
方法一:暴力求解法简单尝试数组每对值的组合求出最小值,时间复杂度O(n2)。
方法二:分治策略
时间复杂度O(nlgn)。将数组划分为两个规模尽量相等的子数组求解。比如找到子数组的中央位置mid,然后考虑求解两个规模尽量相等的子数组A[low...mid]和A[mid+1, high]。那么A[low, high]的任何连续子数组A[i,j]所处位置必然是三种情况之一:
1.完全位于A[low...mid]中;
2.完全位于A[mid+1, high]中;
3.跨越了中点。
1,2两种情况可以可以递归实现,因此剩下的工作就是寻找跨越中点的最大子数组,然后在三种情况中选最大者。
源码如下:
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
struct subarray
{
int low;
int high;
double sum;
};
subarray FindCrossArray(double dAll[], int low, int mid, int high)
{
int max_left, max_right;
double sum = 0, left_sum = dAll[mid], right_sum = dAll[mid + 1];
for (int i = mid; i >= low; i--)
{
sum += dAll[i];
if (sum >= left_sum)
{
left_sum = sum;
max_left = i;
}
}
sum = 0;
for (int j = mid + 1; j <= high; j++)
{
sum += dAll[j];
if (sum >= right_sum)
{
right_sum = sum;
max_right = j;
}
}
struct subarray sa;
sa.low = max_left;
sa.high = max_right;
sa.sum = left_sum + right_sum;
return sa;
}
subarray FindMaximumSubArray(double dAll[], int low, int high)//递归传参
{
struct subarray sa;
if (low == high)
{
sa.low = low;
sa.high = high;
sa.sum = dAll[low];
return sa;
}
else
{
int mid = (low + high) / 2;
struct subarray array_left;
struct subarray array_right;
struct subarray array_cross;
array_left = FindMaximumSubArray(dAll, low, mid);
array_right = FindMaximumSubArray(dAll, mid + 1, high);
array_cross = FindCrossArray(dAll, low, mid, high);
if (array_left.sum > array_right.sum && array_left.sum > array_cross.sum)
return array_left;
else if (array_right.sum > array_left.sum && array_right.sum > array_cross.sum)
return array_right;
else
return array_cross;
}
}
void main()
{
cout << "please enter a squence of number, separated by comma ','! " << endl;
string strInNum;
cin >> strInNum;
strInNum += ",";
double dAllNum[50];
int i = 0;
while (strInNum.find(",") != -1)
{
int pos = strInNum.find(",");
string strNum = strInNum.substr(0, pos);
strInNum = strInNum.substr(pos + 1);
dAllNum[i++] = atof(strNum.c_str());
}
struct subarray result;
result = FindMaximumSubArray(dAllNum, 0, --i);
cout << result.low << " " << result.high << " " << result.sum << endl;
}
方法三:动态规划
时间复杂度O(n)。从数组左边界开始,从左至右处理,记录到目前为止已经处理过的最大子数组。若已经A[0,j]的最大子数组,则A[0,j+1]的最大子数组要么是A[0,j]的最大子数组要么是某个子数组A[i,j+1]。
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
struct SubArray
{
int start;
int end;
double sum;
};
SubArray FindMaxSubArray(double dAllNum[], int nLength)
{
double sum = dAllNum[0], b = dAllNum[0];
int start = 0, end = 0, tmpEnd = 0;
struct SubArray MaxArray;
for (int j = 1; j < nLength; j++)
{
if (b > 0)
{
b += dAllNum[j];
tmpEnd = j;
}
else//若前面最大子数组之和小于0,则丢弃从当前开始,必须保证数组有正才能这么干
{
b = dAllNum[j];
start = end = j;
}
if (b >= sum)
{
sum = b;
end = tmpEnd;
}
}
MaxArray.start = start;
MaxArray.end = end;
MaxArray.sum = sum;
return MaxArray;
}
void main()
{
cout << "please enter a squence of number, separated by comma ','! " << endl;
string strInNum;
cin >> strInNum;
strInNum += ",";
double dAllNum[50];
int i = 0;
while (strInNum.find(",") != -1)
{
int pos = strInNum.find(",");
string strNum = strInNum.substr(0, pos);
strInNum = strInNum.substr(pos + 1);
dAllNum[i++] = atof(strNum.c_str());
}
struct SubArray result;
result = FindMaxSubArray(dAllNum, --i);
cout << result.start << " " << result.end << " " << result.sum << endl;
}