連續子數組的最大和

給出一個整數序列S,其中有N個數,定義其中一個非空連續子序列T中所有數的和爲T的“序列和”。 對於S的所有非空連續子序列T,求最大的序列和。

至少有以下三種解法:

動態規劃

  • 時間複雜度O(n)
void max_subarray_sum(const std::vector<int>& a, std::ostream& os)
{
    int res = INT_MIN;
    int cur_sum = 0;
    for (int i = 0; i < a.size(); i++)
    {
        cur_sum = std::max(cur_sum + a[i], a[i]);
        res = std::max(res, cur_sum);
    }
    os << res;
}

此種解法可以理解爲,假設有最大序列和的子數組是以原數組中第i個元素結尾的,那麼在遍歷的過程中做兩件事:
1. 對於每個i,找到以第i個元素結尾的子數組的最大序列和
2. 對於上述找到的最大序列和,比較得出其中最大的即爲最終結果
代碼中的cur_sum就是爲了做第1件事,而res是爲了做第2件事。得到cur_sum的邏輯爲,以第i個元素結尾的序列和最大的子數組,等於以第(i-1)個元素結尾的序列和最大的子數組加上第i個元素,或者等於第i個元素

掃描法

  • 時間複雜度O(n)
void max_subarray_sum(const std::vector<int> &a, std::ostream &os)
{
    int res = INT_MIN;
    int cur_sum = INT_MIN;
    for (int i = 0; i < a.size(); i++)
    {
        if (cur_sum < 0)
        {
            cur_sum = a[i];
        }
        else
        {
            cur_sum += a[i];
        }
        if(res < cur_sum)
        {
            res = cur_sum;
        }
    }
    os << res;
}

這種掃描法其實和動態規劃是一樣的,因爲,如果cur_sum < 0那麼cur_sum + a[i] < a[i]否則cur_sum + a[i] >= a[i]。同時,這也提供了另一種理解思路,在掃描的過程中逐步求序列和,當求得的和爲負時,將原有的和丟棄,開始重新計算,因爲負的和與後面的值相加,只能使後面的值減小。

分治法

  • 時間複雜度O(nlogn)
int max_subarray_sum_helper(const std::vector<int>& a, int left, int right)
{
    if (abs(left - right) < 2)
    {
        return a[left] > a[right] ? a[left] : a[right];
    }
    int middle = (left + right) / 2;
    int left_max = max_subarray_sum_helper(a, left, middle);
    int right_max = max_subarray_sum_helper(a, middle + 1, right); // (middle + 1) instead of middle 
    LOG(WARNING) << left << " " << middle << " " << right;

    int middle_max = INT_MIN;
    int cur_sum = 0;
    for (int i = middle; i >= left; i--)
    {
        cur_sum += a[i];
        middle_max = cur_sum > middle_max ? cur_sum : middle_max;
    }
    cur_sum = middle_max; // cur_sum should be set to the left_max_sum
    for (int i = middle + 1; i <= right; i++)
    {
        cur_sum += a[i];
        middle_max = cur_sum > middle_max ? cur_sum : middle_max;
    }
    int maxsum = left_max > right_max ? left_max : right_max;
    maxsum = maxsum > middle_max ? maxsum : middle_max;
    LOG(INFO) << "left: " << left << " right: " << right << " max_sum: " << maxsum;
    return maxsum;
}

void max_subarray_sum(const std::vector<int> &a, std::ostream &os)
{
    os << max_subarray_sum_helper(a, 0, a.size() - 1);
}

這裏的分治法的基本思想就是將數組分爲兩部分,最大序列和即爲左邊數組的最大序列和,或者右邊數組的最大序列和,或者是位於中間跨越左右兩個數組的最大序列和

參考博客
1. 求連續子數組的最大和
2. [LeetCode] Maximum Subarray 最大子數組

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章