Leetcode-数据结构-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) {
// 累加sumsum += 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));}
参考
作者: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-数据结构-53.最大子数组和相关推荐
- leetcode系列-53.最大子数组和
题目描述: 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素), 返回其最大和.子数组 是数组中的一个连续部分. 示例 1: 输入:nums = [-2,1,- ...
- LeetCode 53. 最大子数组和【贪心算法、动态规划】
53. 最大子数组和 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 子数组 是数组中的一个连续部分. 示例 1: 输入:nums = [- ...
- 53. 最大子数组和 392.判断子序列 115.不同的子序列
53. 最大子数组和 dp[i]: 0-i-1,包含下标i-1的最大和为dp[i] 若dp[i-1]小于0,则和重新从nums[i]开始计算. 最后返回dp[i]最大值即可 392.判断子序列 i为短 ...
- [LeetCode] Maximum Subarray 最大子数组
Find the contiguous subarray within an array (containing at least one number) which has the largest ...
- 【LeetCode-中等】53. 最大子数组和(详解)
题目 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 子数组 是数组中的一个连续部分. 方法1:动态规划1 作者:guanpengchn 链 ...
- 114. Leetcode 53. 最大子数组和 (动态规划-子序列问题)
步骤一.确定状态: 确定dp数组及下标含义 dp[i]:包括下标i之前的最大连续子序列和为dp[i]. 步骤二.推断状态方程: dp[i]只有两个方向可以推出来: dp[i - 1] + nums[i ...
- 156. Leetcode 53. 最大子数组和 (贪心算法-进阶题目)
class Solution:def maxSubArray(self, nums: List[int]) -> int:result = -float('inf')count = 0for i ...
- 【LeetCode】53.最大子序和
题目 给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 题解 方法1:暴力法 算法 暴力地用两重循环遍历所有情况 代码 class Solution ...
- LeetCode 题 - 53. 最大子序和 python解法
题目 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续 ...
- leetcode之53.最大子序和
题目详情 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: ...
最新文章
- 测序数据学习笔记:bcl2fastq 安装
- 保守的机器学习如何拯救日新月异的我们
- 多维分析中的 UV 与 PV
- 修改ie9默认的quirk模式
- C++ 内存基本构件new/delete的意义、运用方式以及重载方式
- linux查看分区访问权限,linux查看分区是否开启acl权限
- 深入理解JAVA虚拟机——个人阅读笔记
- BAT面试问题--算法工程师(机器学习)
- 数据库原理mysql_数据库原理:MySql的安装
- Sublime Text 无法安装插件
- 上班时间应该包含交通时间
- Spring源码之bean的初始化initializeBean方法解读
- PS——证件照换底色的极简方法
- 源码解析kafka删除topic
- k8s - service
- Windows10切换用户显示User Profile Service或ProfSvc服务登录失败
- gitee搭建个人博客教程
- Android逆向之旅---动态方式破解apk进阶篇(IDA调试so源码)
- 励志: 我们来看看那些优秀的人的眼界和思想
- 第一章:电商及商品系统概述