Skip to content

Latest commit

 

History

History

0918.Maximum Sum Circular Subarray

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

English Version

题目描述

给定一个长度为 n环形整数数组 nums ,返回 nums 的非空 子数组 的最大可能和 

环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i] 的下一个元素是 nums[(i + 1) % n]nums[i] 的前一个元素是 nums[(i - 1 + n) % n]

子数组 最多只能包含固定缓冲区 nums 中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j] ,不存在 i <= k1, k2 <= j 其中 k1 % n == k2 % n 。

 

示例 1:

输入:nums = [1,-2,3,-2]
输出:3
解释:从子数组 [3] 得到最大和 3

示例 2:

输入:nums = [5,-3,5]
输出:10
解释:从子数组 [5,5] 得到最大和 5 + 5 = 10

示例 3:

输入:nums = [3,-2,2,-3]
输出:3
解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3

 

提示:

  • n == nums.length
  • 1 <= n <= 3 * 104
  • -3 * 104 <= nums[i] <= 3 * 104​​​​​​​

解法

方法一:维护前缀最值

求环形子数组的最大和,可以分为两种情况:

  • 情况一:最大和的子数组不包含环形部分,即为普通的最大子数组和;
  • 情况二:最大和的子数组包含环形部分,我们可以转换为:求数组总和减去最小子数组和。

因此,我们维护以下几个变量:

  • 前缀和最小值 $pmi$,初始值为 $0$
  • 前缀和最大值 $pmx$,初始值为 $-\infty$
  • 前缀和 $s$,初始值为 $0$
  • 最小子数组和 $smi$,初始值为 $\infty$
  • 答案 $ans$,初始值为 $-\infty$

接下来,我们只需要遍历数组 $nums$,对于当前遍历到的元素 $x$,我们做以下更新操作:

  • 更新前缀和 $s = s + x$
  • 更新答案 $ans = \max(ans, s - pmi)$,即为情况一的答案(前缀和 $s$ 减去最小前缀和 $pmi$,可以得到最大子数组和);
  • 更新 $smi = \min(smi, s - pmx)$,即为情况二的最小子数组和;
  • 更新 $pmi = \min(pmi, s)$,即为最小前缀和;
  • 更新 $pmx = \max(pmx, s)$,即为最大前缀和。

遍历结束,我们取 $ans$ 以及 $s - smi$ 的最大值作为答案返回即可。

时间复杂度 $O(n)$,其中 $n$ 为数组长度。空间复杂度 $O(1)$

Python3

class Solution:
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        s1 = s2 = f1 = f2 = nums[0]
        for num in nums[1:]:
            f1 = num + max(f1, 0)
            f2 = num + min(f2, 0)
            s1 = max(s1, f1)
            s2 = min(s2, f2)
        return s1 if s1 <= 0 else max(s1, sum(nums) - s2)
class Solution:
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        pmi, pmx = 0, -inf
        ans, s, smi = -inf, 0, inf
        for x in nums:
            s += x
            ans = max(ans, s - pmi)
            smi = min(smi, s - pmx)
            pmi = min(pmi, s)
            pmx = max(pmx, s)
        return max(ans, s - smi)

Java

class Solution {
    public int maxSubarraySumCircular(int[] nums) {
        int s1 = nums[0], s2 = nums[0], f1 = nums[0], f2 = nums[0], total = nums[0];
        for (int i = 1; i < nums.length; ++i) {
            total += nums[i];
            f1 = nums[i] + Math.max(f1, 0);
            f2 = nums[i] + Math.min(f2, 0);
            s1 = Math.max(s1, f1);
            s2 = Math.min(s2, f2);
        }
        return s1 > 0 ? Math.max(s1, total - s2) : s1;
    }
}
class Solution {
    public int maxSubarraySumCircular(int[] nums) {
        final int inf = 1 << 30;
        int pmi = 0, pmx = -inf;
        int ans = -inf, s = 0, smi = inf;
        for (int x : nums) {
            s += x;
            ans = Math.max(ans, s - pmi);
            smi = Math.min(smi, s - pmx);
            pmi = Math.min(pmi, s);
            pmx = Math.max(pmx, s);
        }
        return Math.max(ans, s - smi);
    }
}

C++

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int s1 = nums[0], s2 = nums[0], f1 = nums[0], f2 = nums[0], total = nums[0];
        for (int i = 1; i < nums.size(); ++i) {
            total += nums[i];
            f1 = nums[i] + max(f1, 0);
            f2 = nums[i] + min(f2, 0);
            s1 = max(s1, f1);
            s2 = min(s2, f2);
        }
        return s1 > 0 ? max(s1, total - s2) : s1;
    }
};
class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        const int inf = 1 << 30;
        int pmi = 0, pmx = -inf;
        int ans = -inf, s = 0, smi = inf;
        for (int x : nums) {
            s += x;
            ans = max(ans, s - pmi);
            smi = min(smi, s - pmx);
            pmi = min(pmi, s);
            pmx = max(pmx, s);
        }
        return max(ans, s - smi);
    }
};

Go

func maxSubarraySumCircular(nums []int) int {
	s1, s2, f1, f2, total := nums[0], nums[0], nums[0], nums[0], nums[0]
	for i := 1; i < len(nums); i++ {
		total += nums[i]
		f1 = nums[i] + max(f1, 0)
		f2 = nums[i] + min(f2, 0)
		s1 = max(s1, f1)
		s2 = min(s2, f2)
	}
	if s1 <= 0 {
		return s1
	}
	return max(s1, total-s2)
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func maxSubarraySumCircular(nums []int) int {
	const inf = 1 << 30
	pmi, pmx := 0, -inf
	ans, s, smi := -inf, 0, inf
	for _, x := range nums {
		s += x
		ans = max(ans, s-pmi)
		smi = min(smi, s-pmx)
		pmi = min(pmi, s)
		pmx = max(pmx, s)
	}
	return max(ans, s-smi)
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

TypeScript

function maxSubarraySumCircular(nums: number[]): number {
    let pre1 = nums[0],
        pre2 = nums[0];
    let ans1 = nums[0],
        ans2 = nums[0];
    let sum = nums[0];

    for (let i = 1; i < nums.length; ++i) {
        let cur = nums[i];
        sum += cur;
        pre1 = Math.max(pre1 + cur, cur);
        ans1 = Math.max(pre1, ans1);

        pre2 = Math.min(pre2 + cur, cur);
        ans2 = Math.min(pre2, ans2);
    }
    return ans1 > 0 ? Math.max(ans1, sum - ans2) : ans1;
}
function maxSubarraySumCircular(nums: number[]): number {
    const inf = 1 << 30;
    let [pmi, pmx] = [0, -inf];
    let [ans, s, smi] = [-inf, 0, inf];
    for (const x of nums) {
        s += x;
        ans = Math.max(ans, s - pmi);
        smi = Math.min(smi, s - pmx);
        pmi = Math.min(pmi, s);
        pmx = Math.max(pmx, s);
    }
    return Math.max(ans, s - smi);
}

...