当前位置: 首页 > news >正文

LeetCode算法日记 - Day 82: 环形子数组的最大和

目录

1. 环形子数组的最大和

1.1 题目解析

1.2 解法

1.3 代码实现


1. 环形子数组的最大和

https://leetcode.cn/problems/maximum-sum-circular-subarray/

给定一个长度为 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

1.1 题目解析

题目本质
在环形数组中寻找最大连续子数组和。环形意味着数组首尾相连,但子数组不能重复使用元素。

常规解法
用 动态规划 算法求最大子数组和。遍历数组,维护当前位置结尾的最大和,取全局最大值。

问题分析
普通 动态规划 算法只能处理线性数组,无法处理跨越边界的情况。
例如 [5,-3,5],最优解是选首尾 [5,5]=10,但线性算法只会算出 [5,-3,5]=7。环形数组的最大子数组要么在中间,要么跨越首尾,需要分情况讨论。

思路转折
跨边界的情况可以转换思维——选首尾等价于不选中间。
如果我们能找到中间的最小子数组和,用总和减去它就得到了跨边界的最大和。因此需要同时维护两个 DP:一个求最大子数组和(不跨边界),一个求最小子数组和(用于计算跨边界)。

1.2 解法

算法思想:动态规划分两种情况,f[i] 表示以 i 结尾的最大子数组和,g[i] 表示以 i 结尾的最小子数组和。

状态转移:

  • f[i] = max(f[i-1] + nums[i], nums[i])

  • g[i] = min(g[i-1] + nums[i], nums[i])

最终答案:max(fmax, sum - gmin),其中 fmax 是不跨边界的最大值,sum - gmin 是跨边界的最大值。

i)初始化两个 DP 数组 f 和 g,分别用于记录最大和最小子数组和,初始值设为 0。

ii)第一次遍历计算数组总和 sum,这是计算跨边界情况的基础。

iii)第二次遍历同时更新 f[i] 和 g[i],维护全局最大值 max 和全局最小值 min。

iv)计算跨边界的最大和 tMin = sum - min。如果 tMin == 0 说明所有数都是负数,只能返回 max(最大的负数),否则返回 max(tMin, max)。

易错点

  • f 和 g 的含义理解

    • f[i] 表示以位置 i 结尾的最大子数组和,用于求不跨界情况。

    • g[i] 表示以位置 i 结尾的最小子数组和,用于计算跨界情况(通过 sum - gmin 得到跨界的最大值)。

  • 全负数的边界情况

    • 当数组全为负数时,gmin 会等于 sum(因为整个数组就是最小子数组),导致 sum - gmin = 0。

    • 这个 0 表示"什么都不选",但题目要求子数组非空,所以必须返回 fmax(即最大的那个负数)。判断条件是 tMin == 0 ? max : Math.max(tMin, max)。

  • max 和 min 的初始化:max 不能初始化为 0,必须是 Integer.MIN_VALUE/2,否则当所有数都是负数时,fmax 会被错误地判定为 0。同理,min 初始化为 Integer.MAX_VALUE/2。

  • 跨界计算的本质:sum - gmin 不是简单的数学运算,而是"总和减去不要的最小部分 = 保留的最大部分(跨边界)"。理解这个转换是关键,不要误以为是 sum + |gmin|。

1.3 代码实现

class Solution {public int maxSubarraySumCircular(int[] nums) {int n = nums.length;int[] f = new int[n + 1];  // 最大子数组和int[] g = new int[n + 1];  // 最小子数组和f[0] = g[0] = 0;int max = Integer.MIN_VALUE / 2;int min = Integer.MAX_VALUE / 2;int sum = 0;// 计算总和for (int i = 0; i < n; i++) {sum += nums[i];}// 同时计算最大和最小子数组和for (int i = 1; i <= n; i++) {f[i] = Math.max(f[i - 1] + nums[i - 1], nums[i - 1]);max = Math.max(max, f[i]);g[i] = Math.min(g[i - 1] + nums[i - 1], nums[i - 1]);min = Math.min(min, g[i]);}// 跨边界的最大和 = 总和 - 最小子数组和int tMin = sum - min;// 全是负数时返回最大的负数return tMin == 0 ? max : Math.max(tMin, max);}
}

复杂度分析

  • 时间复杂度:O(n),两次遍历数组。

  • 空间复杂度:O(n),使用了两个 DP 数组。可优化至 O(1),只需维护前一个状态的值。

http://www.dtcms.com/a/519818.html

相关文章:

  • Leetcode 36
  • 深入理解epoll:为什么推荐使用epoll_create1而不是epoll_create?
  • 公司被其它人拿来做网站营销渠道的概念
  • 在 Linux 下使用 I2C(Inter-Integrated Circuit)进行用户态编程 — 教程与实战
  • 替代HT1621B液晶驱动显示模块芯片程序演示
  • C++和OpenGL实现3D游戏编程【连载26】——添加TransformComponent组件(设置子物体的位移、旋转、缩放)
  • 常规条形光源在工业视觉检测上的应用
  • Zotero插件安装
  • Llama Factory、Unsloth与Hugging Face三大微调平台深度对比分析
  • 电脑卡在 “正在准备 Windows”?5 步解决:从等待到重装
  • 优惠券网站要怎么做的佛山禅城网站建设
  • 基于深度学习计算s21参数,在射频中的应用
  • 微服务day01——拆分作业参考
  • YOLO11训练后的模型无法正常推理解决办法
  • 网站模版 优帮云深圳网络安全公司排名
  • navicat过期了,怎么操作mysql。用DBeaver
  • LangGraph工作流与智能体核心模式总结
  • redis常见问题解决
  • 网站顶部有空白网络营销是什么时候出现的
  • NFS文件存储
  • 07_朴素贝叶斯
  • 【源码项目】简单实现的WPF浏览器,有兴趣的可以自己扩展(带源码)
  • 大连做网站哪家好一点商务网站建设用的是什么软件
  • Mybatis一级缓存
  • Java+OpenCV实现图片切割
  • Linux云计算基础篇(24)-PXE批量安装和Kickstart工具
  • 从零搭建 RAG 智能问答系统 6:Text2SQL 与工作流实现数据库查询
  • 创建Django项目
  • 注册个人网站的方法建设企业网站费用
  • 初识C语言13.自定义类型(联合体与枚举)