具体算法详见《算法导论》第三版(中文)67页。
C++代码实现尽量使用数组指针代替下标,头文件程序包含求解问题的函数模板,其中没有用到一个For循环。
头文件代码如下:
#pragma once
#include <limits>
using namespace std;
namespace IntroAlgo3
{
template <typename T>
struct SubArray
{
T *maxleft;
T *maxright;
T sum;
};
template <typename T>
SubArray<T>& Crossing(const T *, T *, T *, T *);
template <typename T>
SubArray<T>& FindMaxSub(T *, T *, T *);
template<typename T>
SubArray<T>& Crossing(const T *A, T *low, T *mid, T *high)
{
T leftsum = numeric_limits<T>::min();
T rightsum = numeric_limits<T>::min();
T sum = 0;
SubArray<T> *ret = new SubArray<T>;
T *cursor = mid;
while (cursor >= low)
{
sum += *cursor;
if (sum > leftsum) {
leftsum = sum;
ret->maxleft = cursor;
}
--cursor;
}
sum = 0;
cursor = mid+1;
while (cursor <= high)
{
sum += *cursor;
if (sum > rightsum) {
rightsum = sum;
ret->maxright = cursor;
}
++cursor;
}
ret->sum = leftsum + rightsum;
return *ret;
}
template<typename T>
SubArray<T>& FindMaxSub(T *A, T *low, T *high)
{
T *mid;
if (low == high) {
SubArray<T> *ret=new SubArray<T>;
ret->maxleft = low;
ret->maxright = high;
ret->sum = *A;
return *ret;
}
else {
SubArray<T> *left = new SubArray<T>;
SubArray<T> *right = new SubArray<T>;
SubArray<T> *cross = new SubArray<T>;
mid = low+(high-low) / 2;
*left = FindMaxSub(A, low, mid);
*right = FindMaxSub(A, mid + 1, high);
*cross = Crossing(A, low, mid, high);
if (left->sum >= right->sum&&left->sum >= cross->sum) {
return *left;
}
else {
if (right->sum >= left->sum&&right->sum >= cross->sum) {
return *right;
}
else {
return *cross;
}
}
}
}
}
调用函数:
int arr[LENGTH_ARR] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
for (size_t i = 0; i < LENGTH_ARR; i++)
{
cout << arr[i] << ",";
}
cout << endl;
SubArray<int> &sa= FindMaxSub(arr, arr, arr + LENGTH_ARR-1);;
cout << sa.maxleft - arr << ", " << sa.maxright - arr << ", " << sa.sum;//打印最大子数组元素和
cout << endl;
结果:
[supervisor@localhost cpp]$ ./Findmax
13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7,
7, 10, 43
上述方法采用递归算法,算法复杂度O(nlogn)。另一种复杂度是线性的算法不采用递归,一次循环就可得出结果,如果n足够大,其速度可达到递归算法的几十倍甚至几百倍。
算法的原理是,输出值初始赋值0,从下标0开始累加子数组的和,如果从头至尾各元素都是负数,则没有结果。
计算子数组的和,比较子数组和与输出值的大小,二者大的存入输出值,并确定子数组右边元素的下标。接下来下标+1,直到子数组和小于0,这时如果扫描数组没有结束,从下一个元素开始将是另一个子数组的起始。调整子数组左右下标,将temp存入左下标,当前下标赋予子数组右下标。见上表。
template<typename T>
SubArray<T>& FindMaxOn(T *A, T *high)
{
T *low = A;
T *temp=0;
SubArray<T> *ret = new SubArray<T>;
ret->sum = 0;
T thisSum = 0;
while (low <= high) {
thisSum += *low;//求子数组的和
if (thisSum > ret->sum) {
ret->sum = thisSum;//子数组的和与输出值比较,大者存入输出值
ret->maxright = low;
ret->maxleft = temp;
}
else if (thisSum < 0) {
thisSum = 0;
temp = low;
}
++low;
}
++ret->maxleft;//temp是在子数组的和小于零的时刻的指针,+1才是下个子数组的起始,left的值来自temp,所以这里+1.
return *ret;
}
调用:
SubArray<int> &sa= FindMaxOn(arr, arr + LENGTH_ARR-1);