leetcode_数据结构_入门_53. 最大子数组和
53.最大子数组和
问题
//给一个整数数组 nums ,
// 请找出一个具有"最大和"的连续子数组(子数组最少包含一个元素),
// 返回其最大和。
//子数组 是数组中的一个"连续部分"
分析
// 这是一道典型的使用「动态规划」
(连续、只要结果)解决的问题,
// 需要我们掌握动态规划问题设计状态的技巧(无后效性),
// 并且需要知道如何推导状态转移方程,
// 最后再去优化空间。
转换为若干个 子问题 连续:可以求出 ”所有“ 经过(以**结尾的)输入数组的 某一个数 的连续子数组的最大和(只要这个结果)。 如果编号为 i 的子问题的结果是负数或者 0 , 那么编号为 i + 1 的子问题就可以把编号为 i 的子问题的结果”舍弃掉“(因为要求是最大和)
代码分析
main函数
package DataStructure_start; public class DS20230109 { public static void main(String[] args) { int[] nums = {-1,9,-2,3,5,6}; System.out.println(maxSubArray(nums)); System.out.println(maxSubArray1(nums)); System.out.println(maxSubArray2(nums)); System.out.println(maxSubArray3(nums)); }
......
}
//方法一:动态规划 // 参考1:(子数组) // 时间复杂度:O(n),N是输入数组的长度
public static int maxSubArray(int[] nums) { int len = nums.length; // dp[i] 表示:以 nums[i] 结尾的连续子数组的最大和 int[] dp = new int[len]; dp[0] = nums[0]; // 如果编号为 i 的子问题的结果是负数或者 0 , // 那么编号为 i + 1 的子问题就可以把编号为 i 的子问题的结果”舍弃掉“(因为要求是最大和) // 即:如果i的子问题的结果是正数,则保留 for (int i = 1; i < len; i++) { // 如果前一个数为正数 if (dp[i - 1] > 0) { // 则再加下一个数 // ?但只是相邻的两个数 × // dp[i] 表示:以 nums[i] 结尾的连续子数组的最大和 // 注意区别dp[](一个连续的数组)与num[](一个数)的区别 dp[i] = dp[i - 1] + nums[i]; } else { dp[i] = nums[i]; } } // 也可以在上面遍历的同时求出 res 的最大值,这里我们为了语义清晰分开写,大家可以自行选择 int res = dp[0]; for (int i = 1; i < len; i++) { res = Math.max(res, dp[i]);//调用函数Math:max函数:判断谁是最大值(三元表达式) } return res;//返回最大和 }
/*补充:
三元表达式 public static int max(int a, int b) { return (a >= b) ? a : b; } 时间复杂度从小到大排序:(由里向外分析时间复杂度;取最大的时间复杂度) O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n) */
// 参考2:优化后 // 时间复杂度:O(n),N是输入数组的长度
public static int maxSubArray1(int[] nums) { int pre = 0;//前一个数 int res = nums[0];//最大和 // 只适用于数组,循环累加(优化点★) for (int num : nums) { pre = Math.max(pre + num, num); res = Math.max(res, pre); } return res; }
/* 有后效性: 如果之前的阶段求解的子问题的结果包含了一些不确定的信息,导致了后面的阶段求解的子问题无法得到,或者很难得到,这叫「有后效性」 解决「有后效性」的办法是固定住需要分类讨论的地方,记录下更多的结果。在代码层面上表现为: 状态数组增加维度; 把状态定义得更细致、准确:状态定义只解决路径来自左右子树的其中一个子树。 需要经常思考 为什么想到需要这样定义状态。 */
/* 动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans 如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字 如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字 每次比较 sum 和 ans的大小,将最大值置为ans,遍历结束返回结果 时间复杂度:O(n) */
public static int maxSubArray3(int[] nums) { int ans = nums[0]; int sum = 0; // 此处类似方法一参考1中的优化部分,即for循环数组 for(int num: nums) { // 正数 if(sum > 0) { // 累加sum sum += num; // 负数,保持不变 } else { sum = num; } // 三元表达式:比较取最大值 ans = Math.max(ans, sum); } return ans; }
// 方法二:分治法(分类讨论,分成三部分) //复杂度分析: //时间复杂度:O(NlogN),这里递归的深度是对数级别的,每一层需要遍历一遍数组(或者数组的一半、四分之一); //空间复杂度:O(logN),需要常数个变量用于选取最大值,需要使用的空间取决于递归栈的深度。 /*
连续子序列的最大和主要由这三部分子区间里元素的最大和得到: 第 1 部分:子区间 [left, mid]; 第 2 部分:子区间 [mid + 1, right]; 第 3 部分:包含子区间 [mid , mid + 1] 的子区间, 即 nums[mid] 与 nums[mid + 1] 一定会被选取(因为跨区。从中间向两边扩散,扩散到底 选出最大值)。 对这三个部分求最大值即可。 */
public static int maxSubArray2(int[] nums) { int len = nums.length; if (len == 0) { return 0; } return maxSubArraySum(nums, 0, len - 1); } private static int maxCrossingSum(int[] nums, int left, int mid, int right) { // 一定会包含 nums[mid] 这个元素 int sum = 0; int leftSum = Integer.MIN_VALUE; // 左半边包含 nums[mid] 元素,最多可以到什么地方 // 走到最边界,看看最值是什么 // 计算以 mid 结尾的最大的子数组的和 for (int i = mid; i >= left; i--) { sum += nums[i]; if (sum > leftSum) { leftSum = sum; } } sum = 0; int rightSum = Integer.MIN_VALUE; // 右半边不包含 nums[mid] 元素,最多可以到什么地方 // 计算以 mid+1 开始的最大的子数组的和 for (int i = mid + 1; i <= right; i++) { sum += nums[i]; if (sum > rightSum) { rightSum = sum; } } return leftSum + rightSum; } private static int maxSubArraySum(int[] nums, int left, int right) { if (left == right) { return nums[left]; } int mid = left + (right - left) / 2; return max3(maxSubArraySum(nums, left, mid), maxSubArraySum(nums, mid + 1, right), maxCrossingSum(nums, left, mid, right)); } private static int max3(int num1, int num2, int num3) { return Math.max(num1, Math.max(num2, num3)); }
ps:注意大括号
参考:
作者:liweiwei1419
链接:https://leetcode.cn/problems/maximum-subarray/solution/dong-tai-gui-hua-fen-zhi-fa-python-dai-ma-java-dai/
来源:力扣(LeetCode)
作者:guanpengchn 链接:https://leetcode.cn/problems/maximum-subarray/solution/hua-jie-suan-fa-53-zui-da-zi-xu-he-by-guanpengchn/ 来源:力扣(LeetCode)
分类:
leetcode_数据结构
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签