469. 环形数组最大子数组和问题

题解:环形数组的最大子数组和


问题描述

我们需要找到一个环形数组的最大子数组和,即找到一段连续的子数组(可能跨越数组的首尾连接)使得其和最大。


解决思路

环形数组的最大子数组和问题,可以分为两种情况讨论:

  1. 子数组没有跨越数组的首尾(普通子数组)。

    • 这种情况下,问题退化为经典的 最大子数组和 问题,可以用 Kadane 算法 高效解决。
  2. 子数组跨越了数组的首尾

    • 这种情况下,最大子数组和可以表示为: [ \text{totalSum} - \text{minSubarraySum} ]
      • totalSum 是数组的总和。
      • minSubarraySum 是子数组的最小和。

因此,我们需要分别计算:

  • 普通的最大子数组和(Kadane 算法)。
  • 数组的总和。
  • 最小子数组和(Kadane 算法的变体)。

最后,结果为以下两者的最大值: [ \text{result} = \max(\text{maxSubarraySum}, \text{totalSum} - \text{minSubarraySum}) ]


特殊情况

如果数组中所有元素都为负数,跨越首尾的子数组和将等于 (\text{totalSum} - \text{minSubarraySum} = 0),这不合法。此时应直接返回普通的最大子数组和。


算法步骤

  1. 使用 Kadane 算法求解普通的最大子数组和(maxSubarraySum)。
  2. 计算数组的总和(totalSum)。
  3. 使用 Kadane 算法求解最小子数组和(minSubarraySum)。
  4. 如果 totalSum == minSubarraySum(所有元素为负数),直接返回 maxSubarraySum
  5. 否则返回: [ \max(\text{maxSubarraySum}, \text{totalSum} - \text{minSubarraySum}) ]

代码实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>

// 使用 Kadane 算法计算最大或最小子数组和
int kadane(const std::vector<int>& nums, bool findMax) {
    int current = nums[0], best = nums[0];
    for (size_t i = 1; i < nums.size(); ++i) {
        if (findMax) {
            current = std::max(nums[i], current + nums[i]);
            best = std::max(best, current);
        } else {
            current = std::min(nums[i], current + nums[i]);
            best = std::min(best, current);
        }
    }
    return best;
}

// 主函数:环形数组的最大子数组和
int solution(std::vector<int>& nums) {
    int n = nums.size();
    
    // Case 1: 最大子数组和(不考虑环形)
    int maxSubarraySum = kadane(nums, true);
    
    // Case 2: 数组的总和
    int totalSum = 0;
    for (int num : nums) {
        totalSum += num;
    }
    
    // Case 3: 最小子数组和
    int minSubarraySum = kadane(nums, false);
    
    // Case 4: 判断是否需要考虑环形情况
    if (totalSum == minSubarraySum) {
        // 如果总和等于最小子数组和,说明所有元素为负数,只能返回 maxSubarraySum
        return maxSubarraySum;
    } else {
        // 否则,返回普通最大子数组和或环形最大子数组和中的较大值
        return std::max(maxSubarraySum, totalSum - minSubarraySum);
    }
}

// 测试用例
int main() {
    std::vector<int> nums1 = {-1, -2, 3, -2};
    std::vector<int> nums2 = {-5, -3, 5};
    std::vector<int> nums3 = {-3, -1, 2, -1};
    std::vector<int> nums4 = {-2, -3, -1};

    std::cout << (solution(nums1) == 3) << std::endl; // 输出:3
    std::cout << (solution(nums2) == 5) << std::endl; // 输出:5
    std::cout << (solution(nums3) == 2) << std::endl; // 输出:2
    std::cout << (solution(nums4) == -1) << std::endl; // 输出:-1
    return 0;
}

代码解析

  1. Kadane 算法

    • 用于求解最大或最小子数组和。
    • 时间复杂度为 (O(n))。
  2. 特殊处理

    • 当数组中所有元素均为负数时,totalSum == minSubarraySum,无法通过环形方式求解。这时直接返回 maxSubarraySum
  3. 结果计算

    • 普通最大子数组和 maxSubarraySum
    • 环形最大子数组和 totalSum - minSubarraySum
    • 返回两者的较大值。

复杂度分析

  1. 时间复杂度

    • Kadane 算法求解最大和最小子数组各需 (O(n))。
    • 计算总和需要 (O(n))。
    • 总时间复杂度:(O(n))。
  2. 空间复杂度

    • 使用常数额外空间。
    • 总空间复杂度:(O(1))。

测试分析

示例 1

输入:nums = [-1, -2, 3, -2]

  • maxSubarraySum = 3(子数组为 [3])。
  • totalSum = -2
  • minSubarraySum = -4(子数组为 [-1, -2])。
  • totalSum - minSubarraySum = 2
  • 输出:max(3, 2) = 3

示例 2

输入:nums = [-5, -3, 5]

  • maxSubarraySum = 5
  • totalSum = -3
  • minSubarraySum = -8
  • totalSum - minSubarraySum = 5
  • 输出:max(5, 5) = 5

示例 3

输入:nums = [-3, -1, 2, -1]

  • maxSubarraySum = 2
  • totalSum = -3
  • minSubarraySum = -5
  • totalSum - minSubarraySum = 2
  • 输出:max(2, 2) = 2

示例 4

输入:nums = [-2, -3, -1]

  • maxSubarraySum = -1
  • totalSum = -6
  • minSubarraySum = -6
  • totalSum == minSubarraySum,返回 `maxSubarraySum =

-1


总结

  1. 问题拆解

    • 普通子数组和问题使用 Kadane 算法解决。
    • 环形子数组和问题通过 totalSum - minSubarraySum 转化。
  2. 关键点

    • 需要处理特殊情况:所有元素为负数时,只能返回普通的最大子数组和。
  3. 算法效率

    • 时间复杂度 (O(n)) 和空间复杂度 (O(1)) 确保算法能够高效处理大规模数组。
  4. 代码优势

    • 简洁明了,易于扩展。
    • 通过分情况处理,覆盖所有可能的输入场景。

此方法是一种优化的、通用的解决方案,可以很好地解决环形数组的最大子数组和问题。

相关推荐
用户605721920981 天前
奇妙货币交易问题 | 豆包MarsCode AI刷题
青训营笔记
我明天再来学Web渗透2 天前
“抖音互联网架构分析及高可用系统构建思考”(方向三)| 豆包MarsCode AI刷题
青训营笔记
用户302133066203 天前
第三次刷题 | 豆包MarsCode AI刷题
青训营笔记
用户9105973027708 天前
CSS详解| 豆包MarsCode AI刷题
青训营笔记
huyck8 天前
伴学笔记1|豆包MarsCode AI 刷题
青训营笔记
用户197009008153813 天前
实现一个TodoList | 青训营 x 豆包MarsCode技术训练营
青训营笔记
幻613 天前
小U的相似字符串 | 豆包MarsCode AI刷题
青训营笔记
Grin1 个月前
寻找最大葫芦 | 豆包MarsCode AI刷题
青训营笔记
用户7337855092591 个月前
后端笔记 | go语言进阶与依赖管理
青训营笔记