1. 程式人生 > >Divide and Conquer : 53. Maximum Subarray

Divide and Conquer : 53. Maximum Subarray

Description

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [-2,1,-3,4,-1,2,1,-5,4], the contiguous subarray [4,-1,2,1] has the largest sum = 6.

設計

這是一道Divide and Conquer型別的題目。使用分治法的話,可以將序列對半分為兩個子序列,遞迴地在子序列中求最大連續和;求出子序列的最大連續和後,從序列的分界點分別向左和向右求出最大連續和並相加,將結果與子序列返回的最大連續和比較,即可得出解。 這種演算法的複雜度遞迴方程是T(n)=2T(n/2)+O(n),根據大師定理,T(n)=O(nlogn)。 題目提示有O(n)複雜度的演算法。考慮先求出從序列首元素到後面每個元素的連續和,用陣列儲存起來。然後遍歷該陣列,記錄當前遇到的最小的連續和,並計算陣列當前下標的連續和減去遇到的最小連續和,大於之前的最大和的話,就替換最大和。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        initializeAnArrayForSavingContiguousSums(nums);
        calculateSumsOfContiguousSubarraysFromZeroToEveryIndex(nums);
        int maxSum = findMaxSumOfSubArrays();
        deleteTheArraySavingContiguousSums();
        return
maxSum; } private: int* contiguousSums; int size; void initializeAnArrayForSavingContiguousSums(vector<int>& nums) { size = nums.size(); contiguousSums = new int[size]; } void calculateSumsOfContiguousSubarraysFromZeroToEveryIndex(vector<int>
& nums) { contiguousSums[0] = nums[0]; for (int i = 1; i < size; i++) contiguousSums[i] = contiguousSums[i - 1] + nums[i]; } int findMaxSumOfSubArrays() { int currentMinContiguousSum = 0; int maxSum = contiguousSums[0]; for (int i = 0; i < size; i++) { if (maxSum < contiguousSums[i] - currentMinContiguousSum) maxSum = contiguousSums[i] - currentMinContiguousSum; if (currentMinContiguousSum > contiguousSums[i]) currentMinContiguousSum = contiguousSums[i]; } return maxSum; } void deleteTheArraySavingContiguousSums() { delete []contiguousSums; size = 0; } };

分析

演算法複雜度為O(n)。但是這種演算法有點複雜,不夠Leetcode該題討論裡的演算法好。引用第2個解答:

this problem was discussed by Jon Bentley (Sep. 1984 Vol. 27 No. 9 Communications of the ACM P885)

the paragraph below was copied from his paper (with a little modifications)

algorithm that operates on arrays: it starts at the left end (element A[1]) and scans through to the right end (element A[n]), keeping track of the maximum sum subvector seen so far. The maximum is initially A[0]. Suppose we’ve solved the problem for A[1 … i - 1]; how can we extend that to A[1 … i]? The maximum sum in the first I elements is either the maximum sum in the first i - 1 elements (which we’ll call MaxSoFar), or it is that of a subvector that ends in position i (which we’ll call MaxEndingHere).

MaxEndingHere is either A[i] plus the previous MaxEndingHere, or just A[i], whichever is larger.

public static int maxSubArray(int[] A) {
    int maxSoFar=A[0], maxEndingHere=A[0];
    for (int i=1;i<A.length;++i){
        maxEndingHere= Math.max(maxEndingHere+A[i],A[i]);
        maxSoFar=Math.max(maxSoFar, maxEndingHere); 
    }
    return maxSoFar;
}

第3和第4個解答也不錯,不過我感覺還是這個解答精簡幹練,而且很好理解。