leetcode 53. 最大子数组和
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
法一:
1.假如全是负数,那就是找最大值即可,因为负数肯定越加越大。
2.如果有正数,则肯定从正数开始计算和,不然前面有负值,和肯定变小了,所以从正数开始。
3.当和小于等于0时,这个区间就告一段落了,然后从下一个正数重新开始计算(也就是又回到 第2步 了)。而 dp 也就体现在这个地方。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int maxSum = nums[0];
int sum = 0,size = nums.size(); //当前最大连续子序列和为sum
for(int i = 0;i < size;i++){
if(sum > 0) sum += nums[i];//如果sum>0,则说明sum对结果有增益效果,则sum保留并加上当前遍历数字
else sum = nums[i];//如果sum<=0,则说明sum对结果无增益效果,需要舍弃,则sum直接更新为当前遍历数字
maxSum = max(maxSum , sum);//如果这一轮循环的nums[i]是负数,sum就可能变小了,所以需要和maxSum比大小
}
return maxSum;
}
};
法二:使用额外空间
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int maxRes = nums[0],size = nums.size();
int dp[size];//dp[i]代表以第 i 个数结尾的「连续子数组的最大和」
dp[0] = nums[0];
for(int i = 1; i < size; i++){
//不是dp[i]=max(dp[i-1],dp[i-1]+nums[i]);而是dp[i]=max(nums[i],dp[i-1]+nums[i]);
dp[i] = max(nums[i], dp[i-1] + nums[i]);
maxRes = max(maxRes, dp[i]);
}
return maxRes;
}
};
法三:递归分治法
class Solution {
public:
int max3(int num1,int num2,int num3){
return max(num1,max(num2,num3));
}
int maxCrossingSum(vector<int>& nums, int left, int mid, int right){
//考虑第 3 部分跨越两个区间的连续子数组的时候,由于 nums[mid] 与 nums[mid + 1] 一定会被选取
//可以从中间向两边扩散,扩散到底选出最大值
int sum = 0;
int leftSum = INT_MIN;
// 左半边包含 nums[mid] 元素,最多可以到什么地方
// 走到最边界,看看最值是什么
// 计算以 mid 结尾的最大的子数组的和
for(int i = mid;i >= left;i--){
sum += nums[i];
if(sum > leftSum) leftSum = sum;
}
sum = 0;
int rightSum = INT_MIN;
// 右半边不包含 nums[mid] 元素,最多可以到什么地方
// 计算以 mid+1 开始的最大的子数组的和
for (int i = mid + 1; i <= right; i++) {
sum += nums[i];
if (sum > rightSum) rightSum = sum;
}
return leftSum + rightSum;
}
int maxSubArraySum(vector<int>& nums,int left,int right){
if(left == right) return nums[left];
int mid = left + (right - left) / 2;//可以很好地防止溢出
//连续子序列的最大和主要由这三部分子区间里元素的最大和得到:
//第 1 部分:子区间 [left, mid];
//第 2 部分:子区间 [mid + 1, right];
//第 3 部分:包含子区间 [mid , mid + 1] 的子区间,即 nums[mid] 与 nums[mid + 1] 一定会被选取。
//对这三个部分求最大值即可。
return max3(maxSubArraySum(nums, left, mid), maxSubArraySum(nums, mid+1, right), maxCrossingSum(nums, left, mid, right));
}
int maxSubArray(vector<int>& nums) {
return maxSubArraySum(nums,0,nums.size()-1);
}
};
法四:前缀和
例如 nums=[1,2,1,2],对应的前缀和数组为 s=[0,1,3,4,6]。
通过前缀和,我们可以把子数组的元素和转换成两个前缀和的差
我们可以一边遍历数组计算前缀和,一边维护前缀和的最小值,用当前的前缀和减去前缀和的最小值,就得到了以当前元素结尾的子数组和的最大值,用它来更新答案的最大值
请注意,由于题目要求子数组不能为空,应当先计算前缀和-最小前缀和,再更新最小前缀和
如果先更新最小前缀和,再计算前缀和-最小前缀和,就会把空数组的元素和 0 算入答案。
示例的 前缀和-最小前缀和的最大值等于 6,即为答案。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//这里使用INT_MIN作为res的初始值,是为了确保任何有效的子数组和都会大于这个初始值,从而能够被正确更新。
int res = INT_MIN;
// 初始化最小前缀和为0,用于跟踪到目前为止的最小前缀和
int minPreSum = 0,
// 初始化当前前缀和为0,用于计算到当前元素为止的数组和
preSum = 0;
for(int &n : nums){
// 更新当前前缀和,加上当前元素的值
preSum += n;
// 更新最大子数组和,通过当前前缀和减去之前遇到的最小前缀和来找到可能的最大值
// 这一步考虑了数组中包含负数的情况,通过从当前前缀和中减去最小前缀和来找到可能的最大子数组和
res = max(res,preSum - minPreSum);
// 更新最小前缀和,保持为到目前为止遇到的最小前缀和
minPreSum = min(minPreSum,preSum);
}
return res;
}
};
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 我与微信审核的“相爱相杀”看个人小程序副业
· DeepSeek “源神”启动!「GitHub 热点速览」
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库