力扣Hot100(2)
力扣Hot100
11、最大子数组和
解法一:动态规划(Kadane 算法)
思路
核心思想:遍历数组,维护以当前元素结尾的最大子数组和(变量 pre)。
对于每个位置 i,要么将当前元素 x 加入到之前的子数组中(pre + x),要么以当前元素重新开始一个新的子数组(x)。取两者最大值,即 pre = max(pre + x, x)。
同时维护全局最大和 maxAns,每次更新 pre 后比较取较大值。
最终 maxAns 即为答案。
class Solution {
public int maxSubArray(int[] nums) {
int pre = 0; // pre 表示以当前元素结尾的最大子数组和(初始为0)
int maxAns = nums[0]; // 全局最大子数组和,初始化为第一个元素(避免数组全负的情况)
for (int x : nums) { // 遍历每个元素
// 关键状态转移:要么继续接上前面的子数组,要么从当前元素重新开始
pre = Math.max(pre + x, x);
// 更新全局最大值
maxAns = Math.max(maxAns, pre);
}
return maxAns;
}
}
复杂度
时间:O(n),一次遍历
空间:O(1)
解法二:分治法(线段树思想)
思路
将数组不断对半分割,直到单个元素。合并时,每个区间需要维护四个信息:
lSum:区间内从左端点开始的最大子数组和(必须包含左端点)
rSum:区间内以右端点结束的最大子数组和(必须包含右端点)
mSum:区间内的最大子数组和(可能完全在左、完全在右、或跨越中点)
iSum:区间内所有元素的总和
合并左右子区间 l 和 r:
iSum = l.iSum + r.iSum
lSum = max(l.lSum, l.iSum + r.lSum) (要么完全在左,要么从左跨到右)
rSum = max(r.rSum, r.iSum + l.rSum)
mSum = max(l.mSum, r.mSum, l.rSum + r.lSum)
最终整个区间的 mSum 即为答案。
class Solution {
// 内部类,用来存储一个区间上的四个统计值
public class Status {
public int lSum, rSum, mSum, iSum;
public Status(int lSum, int rSum, int mSum, int iSum) {
this.lSum = lSum;
this.rSum = rSum;
this.mSum = mSum;
this.iSum = iSum;
}
}
// 对外接口:返回整个数组的最大子数组和
public int maxSubArray(int[] nums) {
return getInfo(nums, 0, nums.length - 1).mSum;
}
// 分治函数:返回区间 [l, r] 的 Status
public Status getInfo(int[] a, int l, int r) {
if (l == r) { // 区间只有一个元素
// 四个值都等于该元素本身
return new Status(a[l], a[l], a[l], a[l]);
}
int m = (l + r) >> 1; // 中间位置(右移1位等效除以2)
Status lSub = getInfo(a, l, m); // 递归处理左半部分
Status rSub = getInfo(a, m + 1, r); // 递归处理右半部分
return pushUp(lSub, rSub); // 合并左右结果
}
// 合并两个子区间的 Status
public Status pushUp(Status l, Status r) {
// 区间总和 = 左总和 + 右总和
int iSum = l.iSum + r.iSum;
// 左端点最大子段和:要么完全在左,要么左全加右的 lSum
int lSum = Math.max(l.lSum, l.iSum + r.lSum);
// 右端点最大子段和:要么完全在右,要么右全加左的 rSum
int rSum = Math.max(r.rSum, r.iSum + l.rSum);
// 区间最大子段和:max(左最大, 右最大, 左rSum+右lSum)
int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
return new Status(lSum, rSum, mSum, iSum);
}
}
复杂度
时间:O(n),递归树共 2n 个节点,每个节点合并 O(1)
空间:O(log n)(递归栈深度)
注意:分治法虽然比 Kadane 复杂,但可以推广到支持动态修改数组元素的场景(线段树)。
浙公网安备 33010602011771号