最大子數組和(最大子段和)

好比對於數組[1,-2,3,5,-1,2] 最大子數組和是sum[3,5,-1,2] = 9, 咱們要求函數輸出子數組和的最大值,而且返回子數組的左右邊界(下面函數的left和right參數).php

本文咱們規定當數組中全部數都小於0時,返回數組中最大的數(也能夠規定返回0,只要讓如下代碼中maxsum初始化爲0便可,此時咱們要注意-1 0 0 0 -2這種情形,特別是若是要求輸出子數組的起始位置時,若是是面試就要和麪試官問清楚)html

如下代碼咱們在PAT 1007. Maximum Subsequence Sum測試經過,測試main函數以下面試

int main()
{
    int n;
    scanf("%d", &n);
    vector<int>vec(n);
    for(int i = 0; i < n; i++)
        scanf("%d", &vec[i]);
    int left, right;
    int maxsum = maxSum1(vec, left, right);//測試時替換函數名稱
    if(maxsum >= 0)
        printf("%d %d %d", maxsum, vec[left], vec[right]);
    else printf("0 %d %d", vec[0], vec[n-1]);
}

參考:編程之美2.14 求數組的子數組之和的最大值算法

 

算法1:最簡單的就是窮舉全部的子數組,而後求和,複雜度是O(n^3)編程

int maxSum1(vector<int>&vec, int &left, int &right)
{
    int maxsum = INT_MIN, sum = 0;
    for(int i = 0; i < vec.size(); i++)
        for(int k = i; k < vec.size(); k++)
        {
            sum = 0;
            for(int j = i; j <= k; j++)
                sum += vec[j];
            if(sum > maxsum)
            {
                maxsum = sum;
                left = i;
                right = k;
            }
        }
    return maxsum;
}

算法2: 上面代碼第三重循環作了不少的重複工做,稍稍改進以下,複雜度爲O(n^2)數組

int maxSum2(vector<int>&vec, int &left, int &right)
{
    int maxsum = INT_MIN, sum = 0;
    for(int i = 0; i < vec.size(); i++)
    {
        sum = 0;
        for(int k = i; k < vec.size(); k++)
        {
            sum += vec[k];
            if(sum > maxsum)
            {
                maxsum = sum;
                left = i;
                right = k;
            }
        }
    }
    return maxsum;
}

算法3: 分治法, 下面貼上編程之美的解釋, 複雜度爲O(nlogn)函數

image

image

//求數組vec【start,end】的最大子數組和,最大子數組邊界爲[left,right]
int maxSum3(vector<int>&vec, const int start, const int end, int &left, int &right)
{
    if(start == end)
    {
        left = start;
        right = left;
        return vec[start];
    }
    int middle = start + ((end - start)>>1);
    int lleft, lright, rleft, rright;
    int maxLeft = maxSum3(vec, start, middle, lleft, lright);//左半部分最大和
    int maxRight = maxSum3(vec, middle+1, end, rleft, rright);//右半部分最大和
    int maxLeftBoeder = vec[middle], maxRightBorder = vec[middle+1], mleft = middle, mright = middle+1;
    int tmp = vec[middle];
    for(int i = middle-1; i >= start; i--)
    {
        tmp += vec[i];
        if(tmp > maxLeftBoeder)
        {
            maxLeftBoeder = tmp;
            mleft = i;
        }
    }
    tmp = vec[middle+1];
    for(int i = middle+2; i <= end; i++)
    {
        tmp += vec[i];
        if(tmp > maxRightBorder)
        {
            maxRightBorder = tmp;
            mright = i;
        }
    }
    int res = max(max(maxLeft, maxRight), maxLeftBoeder+maxRightBorder);
    if(res == maxLeft)
    {
        left = lleft;
        right = lright;
    }
    else if(res == maxLeftBoeder+maxRightBorder)
    {
        left = mleft;
        right = mright;
    }
    else
    {
        left = rleft;
        right = rright;
    }
    return res;
}

算法4: 動態規劃, 數組爲vec[],設dp[i] 是以vec[i]結尾的子數組的最大和,對於元素vec[i+1], 它有兩種選擇:a、vec[i+1]接着前面的子數組構成最大和,b、vec[i+1]本身單獨構成子數組。則dp[i+1] = max{dp[i]+vec[i+1],  vec[i+1]}測試

若是不考慮記錄最大子數組的位置,因而有如下代碼:                本文地址3d

 

int maxSum_(vector<int>&vec)
{
    int maxsum = INT_MIN, sum = 0;
    for(int i = 0; i < vec.size(); i++)
    {
        sum = max(sum + vec[i], vec[i]);
        maxsum = max(maxsum, sum);
    }
    return maxsum;
}

 

對以上代碼換個寫法,並記錄最大子數組的位置htm

int maxSum4(vector<int>&vec, int &left, int&right)
{
    int maxsum = INT_MIN, sum = 0;
    int begin = 0;
    for(int i = 0; i < vec.size(); i++)
    {
        if(sum >= 0)
        {
            sum += vec[i];
        }
        else
        {
            sum = vec[i];
            begin = i;
        }

        if(maxsum < sum)
        {
            maxsum = sum;
            left = begin;
            right = i;
        }
    }
    return maxsum;
}

若是數組是循環的,該如何呢

這時分兩種情形(圖中紅色方框表示求得的最大子數組,left、right分別是子數組的開始和結尾):

(1)以下圖最大的子數組沒有跨過vec[n-1]到vec[0], 這就是每循環的狀況

image

(2)以下圖,最大的子數組跨過vec[n-1]到vec[0]

image

對於第二種情形,至關於從原數組中挖掉了一塊(vec[right+1], …, vec[left-1]) ,那麼咱們只要使挖掉的和最小便可,求最小子數組和最大子數組相似,代碼以下,如下代碼在九度oj1572首尾相連數組的最大子數組和經過測試(測試須要,如下代碼當數組全是負數時,輸出0):

int maxSumCycle(vector<int>&vec, int &left, int&right)
{
    int maxsum = INT_MIN, curMaxSum = 0;
    int minsum = INT_MAX, curMinSum = 0;
    int sum = 0;
    int begin_max = 0, begin_min = 0;
    int minLeft, minRight;
    for(int i = 0; i < vec.size(); i++)
    {
        sum += vec[i];
        if(curMaxSum >= 0)
        {
            curMaxSum += vec[i];
        }
        else
        {
            curMaxSum = vec[i];
            begin_max = i;
        }

        if(maxsum < curMaxSum)
        {
            maxsum = curMaxSum;
            left = begin_max;
            right = i;
        }
        ///////////////求和最小的子數組
        if(curMinSum <= 0)
        {
            curMinSum += vec[i];
        }
        else
        {
            curMinSum = vec[i];
            begin_min = i;
        }

        if(minsum > curMinSum)
        {
            minsum = curMinSum;
            minLeft = begin_min;
            minRight = i;
        }
    }
    if(maxsum >= sum - minsum)
        return maxsum;
    else
    {
        left = minRight+1;
        right = minLeft-1;
        return sum - minsum;
    }
}

 

參考:面試題精解之二: 字符串、數組(1)

 

【版權聲明】轉載請註明出處:http://www.cnblogs.com/TenosDoIt/p/3698246.html

相關文章
相關標籤/搜索