53. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
JAVA
思路1:
- sums存储nums的和,maxval存储它的最大值
- 当sums比maxval大的时候,赋值给maxval
- 运用了双层循环
class Solution {
public int maxSubArray(int[] nums) {
int maxval = nums[0];
for(int i =0;i<nums.length;i++){
int sums = nums[i];
for (int j = i+1;j<nums.length;j++){
sums = sums +nums[j];
if (sums>maxval)
maxval =sums;
}
if(maxval<nums[i])
maxval = nums[i];
}
return maxval;
}
}
思路2:
- 假设我们的sums = sums + nums[i],如果我们的sums>0的话,那么它对于求最大值是有效的,所以我们要继续加下去,但如果已经小于0了 则当做累赘丢弃不要 。
- 这种方法比上一种少了一层循环。
class Solution {
public int maxSubArray(int[] nums) {
int maxval=nums[0];
int sums = 0;
for(int i=0;i<nums.length;i++){
if(sums>0){
sums = sums+nums[i];
}else{
sums = nums[i];
}
maxval = Math.max(sums,maxval);
}
return maxval;
}
}
思路3:
分治法思想就是将问题分解成更小的问题,经过层层递归,最后进行整合每层递归将数组均匀分成两半,三种情况:
1.最大子序和在左边
2.最大子序和在右边
3.最大子序和横跨左右
Divide:取当前数组的中间位mid将数组分成左右两半
Conquer:递归求解左右两半的最大子序和,当子问题size为 1 时,直接返回
Combine:合并左右两边以及中间的解,向上返回
class Solution {
public int maxSubArray(int[] nums) {
//分治法
//每层递归将数组均匀分成两半,三种情况:
//1.最大子序和在左边
//2.最大子序和在右边
//3.最大子序和横跨左右
return maxSubArray1(nums, 0, nums.length - 1);
}
public int maxSubArray1(int[] nums, int l, int r){
//分解到只有一个数时,返回该值
if(l == r)
return nums[l];
int mid = (l + r) / 2;
//递归求左右最大子序和
int resL = maxSubArray1(nums, l, mid);
int resR = maxSubArray1(nums, mid + 1, r);
// 从mid向两边求最大子序和maxSumL,maxSumR,第三种情况最大子序和是maxSumL + maxSumR
int maxSumL = nums[mid]; int maxSumR = nums[mid + 1];
int sumL = 0;
int sumR = 0;
//计算mid左边的最大子序和
for(int i = mid; i >= l; i--){
sumL += nums[i];
if(sumL > maxSumL)
maxSumL = sumL;
}
//计算mid右边的最大子序和
for(int j = mid + 1; j <= r; j++){
sumR += nums[j];
if(sumR > maxSumR)
maxSumR = sumR;
}
int resM = maxSumL + maxSumR;
//返回resL, resM, resR三者最大值
return Math.max(resM, (resL > resR ? resL : resR));
}
}
Python
思路4:
示例: [a, b , c, d , e]
解答这类题目, 省略不掉遍历, 因此我们先从遍历方式说起
通常我们遍历子串或者子序列有三种遍历方式
以某个节点为开头的所有子序列: 如 [a],[a, b],[ a, b, c] ... 再从以b为开头的子
序列开始遍历 [b] [b, c]。
根据子序列的长度为标杆,如先遍历出子序列长度为 1 的子序列,在遍历出长度为2的等等。
以子序列的结束节点为基准,先遍历出以某个节点为结束的所有子序列,因为每个节点都可
能会是子序列的结束节点,因此要遍历下整个序列,如: 以 b 为结束点的所有子序列:
[a , b] [b] 以 c 为结束点的所有子序列: [a, b, c] [b, c] [ c ]。
// 在每一个扫描点计算以该点数值为结束点的子数列的最大和(正数和)。
// 该子数列由两部分组成:以前一个位置为结束点的最大子数列、该位置的数值。
// 因为该算法用到了“最佳子结构”(以每个位置为终点的最大子数列都是基于其前一位置的最大子数列计算得出
// 该算法可看成动态规划的一个例子。
// 状态转移方程:sum[i] = max{sum[i-1]+nums[i],nums[i]}
// 其中(sum[i]记录以nums[i]为子序列末端的最大序子列连续和)
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
maxval = nums[0]
sums = 0
for i in range(len(nums)):
if sums+nums[i]>nums[i]:
sums = sums+nums[i]
else:
sums = nums[i]
maxval = max(maxval,sums)
return maxval