原题目链接:连续子数组的最大和
快点击这里征服第二道!!!
题目描述:
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
提示:
1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
做题思路:
1. 动态规划简单介绍
动态规划(Dynamic Programming)简称dp,是求解最优化问题的一种常用策略,通常来说,使用动态规划去解决问题无非就是三步
1.1 定义状态(状态是原问题,子问题的解),比如定义dp(i)的含义
1.2 设置初始状态(边界),比如设置dp(0)的值
1.3 确定状态转移方程,比如确定dp(i) 和dp(i - 1)的关系
2. 那么利用这三步,让我们看看如何解决上面这道题。
2.1 假设nums的值是{-2,1,-3,4,-1,2,1,-5,4},显而易见,最大连续子序列和是4+(-1)+2+1 = 6
2.2 定义状态
假设dp(i)是以nums[i]结尾的最大连续子序列和,什么意思呢?例如
nums[0],-2结尾的最大连续子序列是-2,所以dp(0) = -2
nums[1],1结尾的最大连续子序列是1,所以dp(1) = 1
nums[2],-3结尾的最大连续子序列是1,-3,所以dp(2) = dp(1)+(-3) = -2
nums[3],4结尾的最大连续子序列是4,所以dp(3) = 4
nums[4],-1结尾的最大连续子序列是4,-1,所以dp(4) = dp(3)+(-1) = 3
nums[5],2结尾的最大连续子序列是4,-1,2,所以dp(5) = dp(4)+ 2 = 5
nums[6],1结尾的最大连续子序列是4,-1,2,1,所以dp(6) = dp(5)+ 1 = 6
nums[7],-5结尾的最大连续子序列是4,-1,2,1,-5,所以dp(7) = dp(6)+ (-5) = 1
nums[8],4结尾的最大连续子序列是4,-1,2,1,所以dp(6) = dp(7)+ 4 = 5
所以我们可以发现规律,其实只要是前面的最大连续子序列和是大于0的话,我们就要,如果小于等于0,就不要了
2.3 状态转移方程
如果dp(i - 1)<= 0,那么dp(i)= nums[i];如果dp(i - 1)> 0,那么dp(i)= dp(i - 1)+ nums[i];
2.4 初始状态
dp(0)的值是nums[0]
2.5 所以最终的解肯定是最大连续子序列和dp(i)中的最大值,也就是max{dp(i)},i∈[0,nums.length)·
3. 如果觉得太晦涩的话,接下来我就用大白话讲解
例如你现在在打王者荣耀,nums[]数组里面元素都是各个玩家的战斗力,dp(i)里面是以i结尾的组成战队的最强战斗力,因为王者荣耀是组队战斗,你要挑选战队,你是不是肯定会选择比你战斗力高的战队加入,躺赢嘛,谁不想呢,所以你一旦遇到战队战斗力没有比你大的,也就是小于等于0,就不要,遇到战斗力比你大的,你就加入战队,但是喔,因为一个战队你加了,别人也会加,有一些战斗力小于0的,比你还菜的人也想抱你们的大腿,那这样的人对于你们战队来说,是累赘啊,而且你无法判断下一个加进来的人,战斗力如何,所以等全部玩家都加入了各自的战队后,你会发现一定有最强战斗力的战队,此时,你就可以选择加入他们,强强联手,岂不是乱杀?!!
废话不多说,直接上代码,为了让各位看官更能清晰理解,我的代码写得不精简,我的代码里加了大量的注释,相信各位看官可以理解,如果我有些没写清楚或者写错的,可以评论区或者私信我喔
class Solution {
//记住一句话,递推就是由底向上的计算过程
//假设dp(i)是以nums[i]结尾的最大连续子序列和(nums是整个序列)
//例如dp[4]是以nums[4]结尾的最大连续子序列和
public int maxSubArray(int[] nums) {
//如果数组只有一个元素,那么最大和肯定只有这个元素了,就直接返回
if(nums.length < 2) return nums[0];
//取出第一个元素
int dp = nums[0];
//假设第一个元素是最大和
int max = dp;
for(int i = 1; i < nums.length; i++){ //遍历后面的元素
if(dp <= 0){ //如果遍历到某个元素,发现前面的最大连续子序列和是小于等0
//那么这个元素就是当前的最大连续子序列和,因为很简单,前面都是负数了,我还要来干嘛,如果这个元素加上他们,岂不是更小了?所以不如不要
dp = nums[i];
}else{
//发现前面的最大连续子序列和是大于0,那么我们就尝试把这个元素加进去
/*
那可能会有这么个疑惑,假如当前元素是小于0呢?加进去岂不是会让之前的最大连续子序列和更小?其实不会,因为下面关于max代码就是会在max和dp取较大值,
由于在执行dp = dp + nums[i]之前,max肯定是之前的最大连续子序列和,所以即使当前元素是小于0也不怕,因为执行下面代码的时候,max应该是等于max而不是dp
*/
dp = dp + nums[i];
}
//这里取较大和
max = Math.max(max,dp);
}
//返回最大和
return max;
}
}
都看到这里了,不考虑点个赞再走嘛?如果大家不点赞的话,我的心就是冰冰的了