求解最大連續子數組(分治策略)

求解最大連續子數組(分治策略)

算法思想

基於分治策略,將原始問題分解爲多個更小規模的子問題,將數組以中心爲間隔分爲左子數組和右子數組,假設原始問題規模爲n,此時將問題分爲兩個規模n/2的子問題,分別遞歸地求解兩個子問題,兩個子數組的最大連續數組,遞歸直到數組只有一個元素返回(通過查找最大連續過中點子樹函數得到),保證得到左右最大連續子數組,回溯後,再考慮跨過中點的中子數組,最後選擇左子數組、右子數組和中子數組中最大的作爲當前子數組最大連續子數組,在回溯到上一級,求解出原問題的所有子問題,即可得到最大原始問題的解。
實際上,最大連續子數組有三種情況,分別是在左子數組中、右子數組中、跨過中點的子數組中,考慮這三種情況即可

實現代碼

/*
author : eclipse
email  : [email protected]
time   : Sun Jun 14 11:18:17 2020
*/
#include <bits/stdc++.h>
using namespace std;

class MaxSubarray {
private:
    vector<int> array;
    int findMaxCrossingSubarray(int low, int mid, int high, int &maxLeft, int &maxRight);
    int findMaxSubarray(int low, int high, int &boundaryLow, int &boundaryHigh);
public:
    MaxSubarray(vector<int> v);
    void findMaxSubarray();
};

MaxSubarray::MaxSubarray(vector<int> v) {
    for (int i = 0; i < v.size(); i++) {
        array.push_back(v[i]);
    }
}

int MaxSubarray::findMaxCrossingSubarray(int low, int mid, int high, int &maxLeft, int &maxRight) {
    int leftSum = -INT_MAX;
    int sum = 0;
    for (int i = mid; i >= low; i--) {
        sum += array[i];
        if (sum > leftSum) {
            leftSum = sum;
            maxLeft = i;
        }
    }
    int rightSum = -INT_MAX;
    sum = 0;
    for (int i = mid + 1; i <= high; i++) {
        sum += array[i];
        if (sum > rightSum) {
            rightSum = sum;
            maxRight = i;
        }
    }
    return leftSum + rightSum;
}

int MaxSubarray::findMaxSubarray(int low, int high, int &boundaryLow, int &boundaryHigh) {
    if (low == high) {
        boundaryLow = low;
        boundaryHigh = high;
        return array[low];
    } else {
        int mid = (low + high) / 2;
        int leftLow, leftHigh, leftSum;
        leftSum = findMaxSubarray(low, mid, leftLow, leftHigh);
        int rightLow, rightHigh, rightSum;
        rightSum = findMaxSubarray(mid + 1, high, rightLow, rightHigh);
        int crossLow, crossHigh, crossSum;
        crossSum = findMaxCrossingSubarray(low, mid, high, crossLow, crossHigh);
        if (leftSum >= rightSum && rightSum >= crossSum) {
            boundaryHigh = leftHigh;
            boundaryLow = leftLow;
            return leftSum;
        } else if (rightSum >= leftSum && rightSum >= crossSum) {
            boundaryHigh = rightHigh;
            boundaryLow = rightLow;
            return rightSum;
        } else {
            boundaryHigh = crossHigh;
            boundaryLow = crossLow;
            return crossSum;
        }
    }
}

void MaxSubarray::findMaxSubarray() {
    int boundaryLow, boundaryHigh, maxSum;
    maxSum = findMaxSubarray(0, array.size() - 1, boundaryLow, boundaryHigh);
    printf("%d\n", maxSum);
    for (int i = boundaryLow; i <= boundaryHigh; i++) {
        printf("%d ", array[i]);
    }
}

int main(int argc, char const *argv[]) {
    int N;
    vector<int> v;
    scanf("%d", &N);
    for (int i = 0; i < N; i++) {
        int value;
        scanf("%d", &value);
        v.push_back(value);
    }
    MaxSubarray *maxSubarray = new MaxSubarray(v);
    maxSubarray->findMaxSubarray();
    return 0;
}

輸入數據

9
-1 -1 2 3 4 -5 23 -1 2

輸出結果

28
2 3 4 -5 23 -1 2

樣例圖解

框中的爲當前子數組最大連續子數組

鳴謝

《算法導論》

最後

  • 由於博主水平有限,不免有疏漏之處,歡迎讀者隨時批評指正,以免造成不必要的誤解!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章