求解最大子数组问题 -- 暴力求解 和 分治法求解

/*------------------  求解最大子数组问题  ---------------
    最大子数组,就是求解一个数组的所有元素的各种组合中,和最大的
那个子数组。在这种情况下,如果元素值全部非负,那么最大子数组当然
是所有元素。但是如果有负值的元素存在,那么久需要找到一个由数组中
连续几个元素组成的子数组并且其元素值之和最大。
*/
#include <iostream>
using namespace std;

struct ArrayStruct {
    ArrayStruct(int b = 0, int e = 0, int s = 0):
                begin(b), end(e), sum(s) { }
    int begin;
    int end;
    int sum;
};

/*
方法一: 首先,最简单的当然是暴力求解,这种情况下,算法的复杂度是
ø(n^2)的,代码如函数 subArrayRough 所示。 
*/
ArrayStruct subArrayRough(int *array, int length) {
    int sum = -999999;
    int begin, end;
    for (int i = 0; i != length; ++i) {
        int sumTemp = 0;
        for (int j = i; j != length; ++j) {
            sumTemp += array[j];
            if (sum < sumTemp) {
                begin = i;
                sum = sumTemp;
                end = j;
            }
        }
    }
    return ArrayStruct(begin, end, sum);
}

/*
方法二是采用分治法:
    加入我们寻找数组 A[low, high] 的最大子数组,使用分治技术意味着
把原数组进行分割,也就是将原数组划分为规模尽量相同的子数组。那么,就需
要找到原数组的重点 mid,然后分别对原数组的 mid 左边和右边进行处理。
那么,最大子数组所处的位置必然是下面三种情况之一【i, j 为结果范围下标】:
    · 完全位于子数组 A[low, mid]中,因此 low <= i <= j <= mid.
    · 完全位于子数组A[mid + 1, high] 中,因此 mid < i <= j <= high.
    · 跨越中点,因此 low <= i <= mid < j <= high
实际上,数组 A 的最大子数组必然是上面三种情况中的最大者。那么,我们只需要找
到上面三种情况的最大者即可。
    那么,我们就可以递归地求解子数组 A[low, mid],A[mid + 1, high] 的最大
子数组,因为这仍然是求解最大子数组问题,然后将这两者的值和两者和中点组成的组数组
三者中寻找最大值。那么,我们就要处理三种情况,A[low, mid],A[mid + 1, high] 和
包括中点三种情况。伪代码如下:
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-CROSSING-SUBARRAY(A, low, mid, high)
        if left-sum >= right-sum && left-sum >= cross-sum:
            return (left-low, left-high, left-sum)
        if right-sum >= right-sum && right-sum >= cross-sum:
            return (right-low, right-high, right-sum)
        else
            return (cross-low, cross-high, cross-sum)

其中,函数 FIND-MAXIMUM-CROSSING-SUBARRAY 的伪代码如下,其实它的作用就是找出一定包含 mid 元素的
的最大子数组,那么久可以直接从 mid 出发去向两边查找:
FIND-MAXIMUM-CROSSING-SUBARRAY(A, low, mid, high):
    left-sum = -∞
    sum = 0;
    for i = mid downto low:
        sum += A[i]
        if left-sum < sum:
            left-sum = sum
            max-left = i
    right-sum = -∞
    sum = 0;
    for i = mid + 1 to high:
        sum += A[i]
        if right-sum < sum:
            right-sum = sum
            max-right = i
    return (max-left , max- right, left-sum + right-sum)

根据伪代码不难写出代码。

FIND-MAXIMUM-CROSSING-SUBARRAY 的时间复杂度是线性的,而 FIND-MAXIMUM-SUBARRAY
因为是分治法,对于分治法算时间复杂度的主方法 T(n) = aT(n / b) + f(n) 可以知道,因为
a == b == 2 所以时间复杂度是 log(n),所以总的时间复杂度是 n(log(n))
*/

ArrayStruct findMaximumCrossingSubArray(int *array, int low, int mid, int high) {
    int leftSum = -99999;
    int sum = 0, leftMax = low;
    for (int i = mid; i >= 0; --i) {
        sum += array[i];
        if (sum > leftSum) {
            leftSum = sum;
            leftMax = i;
        }
    }

    int rightSum = -99999;
    sum = 0;
    int rughtMax = high;
    for (int i = mid + 1; i <= high; ++i) {
        sum += array[i];
        if (sum > rightSum) {
            rightSum = sum;
            rughtMax = i;
        }
    }
    return ArrayStruct(leftMax, rughtMax, leftSum + rightSum);
}

ArrayStruct findMaximumSubArray(int *array, int low, int high) {
    if (high == low) {
        return ArrayStruct(low, high, array[low]);
    } else {
        int mid = (low + high) / 2;
        ArrayStruct left = findMaximumSubArray(array, low, mid);
        ArrayStruct right = findMaximumSubArray(array, mid + 1, high);
        ArrayStruct cross = findMaximumCrossingSubArray(array, low, mid, high);

        if (left.sum >= right.sum && left.sum >= cross.sum)
            return left;
        else if (right.sum >= right.sum && right.sum >= cross.sum)
            return right;
        else
            return cross;
    }
}

int main(int argc, char const *argv[])
{
    cout << "Input the size of the array: ";
    int arraySize;
    int *array;
    cin >> arraySize;
    array = new int[arraySize];
    for (int i = 0; i != arraySize; ++i)
        cin >> array[i];
    ArrayStruct a = subArrayRough(array, arraySize);
    cout << "result of rough found: " << endl << '\t';
    for(int i = a.begin; i <= a.end; ++i) {
        cout << array[i] << " ";
    }
    cout << endl;
    cout << "result of minute found: " << endl << '\t';
    ArrayStruct b = findMaximumSubArray(array, 0, arraySize - 1);
    for(int i = b.begin; i <= b.end; ++i) {
        cout << array[i] << " ";
    }
    cout << endl;

    return 0;
}

 

posted @ 2014-11-02 19:55  厕所门口~~  阅读(828)  评论(0编辑  收藏  举报