LeetCode 53. Maximum Subarray

LeetCode 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.

暴力枚举,时间复杂度  O(n3)

// 伪代码
for i ← 1 to n
    for j ← i to n
        suma[i]+..+a[j]
        ans ← max(ans, sum)
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int ans = -2147483647;
        for (int st = 0; st < n; ++st)
            for (int ed = st + 1; ed <= n; ++ed){
                int sum = 0;
                for (int i = st; i < ed; ++i)
                    sum += nums[i];
                if (ans < sum)
                    ans = sum;
            }
        return ans;
    }
};

注意ans的设置。(int最小值-2147483648) 注意st, ed, i的区间。
Submission Details: 200 / 202 test cases passed. Status: Time Limit Exceeded
案例通过了,排除了死循环的嫌疑。但仍超时,说明时间复杂度太高。
三重循环,每重都是  O(n) ,因此时间复杂度是  O(n3) 。附加空间复杂度  O(1)

优化枚举,时间复杂度  O(n2)

优化代码时一般找最内层的循环,因为最内层的循环被执行的次数最多。

// 伪代码
for i ← 1 to n
    sum0
    for j ← i to n
        sumsum + a[j]
        ans ← max(ans, sum)
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int ans = -2147483647;
        for(int st = 0; st < n; ++st){
            int sum = 0;
            for(int ed = st + 1; ed <= n; ++ed){
                sum += nums[ed - 1];
                if (sum > ans)
                    ans = sum;
            }
        }
        return ans;
    }
};

Submission Details: 202 / 202 test cases passed. Status: Time Limit Exceeded
(比刚才多过了两个案例,LeetCode没有做好边界检测。)
仍然超时。时间复杂度  O(n2) 。附加空间复杂度  O(1)

继续优化,时间复杂度  O(n)

很多算法书上给出了如下的伪代码:

// 伪代码
sum0      ans ← 0
for i ← 1 to n
    sumsum + a[i]
    ans ← max(sum, ans)
    if (sum < 0)  sum0

晦涩难懂。用去冗余的思路重写代码。
我们要求 max {a[i] + … + a[j]},设s[i] = a[0] + … + a[i],则问题转化为求max {s[j] - s[i-1]}。
若j固定(可以枚举j),s[j] = p,则max {s[j] - s[i-1]} = p - min s[i-1] (i < j)。
又,在寻找最小值的时候,有 min[0,6] = min(min[0,5] , min[6,6])。于是有下面的算法:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int ans = -2147483647;
        int sj = 0;
        int minSi = 0;
        int si = 0;
        for (int j = 0; j < n; ++j){
            sj += nums[j];
            if (si < minSi)
                minSi = si;
            if (sj - minSi > ans){
                ans = sj - minSi;
            }
            si += nums[j];
        }
        return ans;
    }
};

Submission Details: 202 / 202 test cases passed. Status: Accepted
Runtime: 13 ms. Your runtime beats 23.20% of cpp submissions.

si和sj非常像,在for循环体的最后一句它们是相等的。可以继续优化。
变量替换,将sj替换为si + nums[j],于是有:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int ans = -2147483647;
        int minSi = 0;
        int si = 0;
        for (int j = 0; j < n; ++j){
            if (si < minSi)
                minSi = si;
            if (si + nums[j] - minSi > ans){
                ans = si + nums[j] - minSi;
            }
            si += nums[j];
        }
        return ans;
    }
};

Submission Details: 202 / 202 test cases passed. Status: Accepted
Runtime: 9 ms. Your runtime beats 57.56% of cpp submissions.

我们新设一个int变量sum,表示si - minSi,那么上面的代码就可以改成:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int ans = -2147483647;
        int sum = 0;
        for (int j = 0; j < n; ++j){
            if (sum < 0)
                sum = 0;
            if (sum + nums[j] > ans){
                ans = sum + nums[j];
            }
            sum += nums[j];
        }
        return ans;
    }
};

Submission Details: 202 / 202 test cases passed. Status: Accepted
Runtime: 6 ms. Your runtime beats 98.41% of cpp submissions.

上面的代码又可以改成:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int ans = -2147483647;
        int sum = 0;
        for (int j = 0; j < n; ++j){
             sum += nums[j];
            if (sum > ans)
                ans = sum; 
            if (sum < 0)
                sum = 0;
        }
        return ans;
    }
};

从而与之前的伪代码一致。

你可能感兴趣的:(LeetCode)