53. (Maximum Subarray)最大子序和
Posted OIqng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了53. (Maximum Subarray)最大子序和相关的知识,希望对你有一定的参考价值。
题目:
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
A subarray is a contiguous part of an array.
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
Example 1:
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 最大的和为 6 。
Example 2:
Input: nums = [1]
Output: 1
示例 2:
输入:nums = [1]
输出:1
Example 3:
Input: nums = [5,4,-1,7,8]
Output: 23
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
Constraints:
- 1 <= nums.length <= 3 * 1 0 4 10^4 104
- - 1 0 5 10^5 105 <= nums[i] <= 1 0 5 10^5 105
提示:
- 1 <= nums.length <= 3 * 1 0 4 10^4 104
- - 1 0 5 10^5 105 <= nums[i] <= 1 0 5 10^5 105
Follow up:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
进阶:
如果您已经找到了O(n)解决方案,请尝试使用分治方法编写另一个解决方案,看看哪个更微妙。
解题思路:
方法一:贪心算法
贪心算法:做出当前最优解,即某种意义上的局部最优解。
就本题来说:求最大子序和来说当数组nums =[-2,1,-3,4,-1,2,1,-5,4]计算子序和的时候一定是从1开始(即前一个子序和丢弃),而当子序和小于0时则放弃当前子序和从下一个元素开始计算。
python代码
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
pre = -float('inf')
subMax = 0
for i in range(len(nums)):
subMax += nums[i]
if subMax > pre:
pre = subMax
if subMax <= 0:
subMax = 0
return pre
Java代码
class Solution {
public int maxSubArray(int[] nums) {
if (nums.length == 1){
return nums[0];
}
int pre = Integer.MIN_VALUE;
int subMax = 0;
for (int i = 0; i < nums.length; i++){
subMax += nums[i];
pre = Math.max(pre, subMax);
if (subMax <= 0){
subMax = 0;
}
}
return pre;
}
}
C++代码
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = INT_MIN;
int subMax = 0;
for (int i = 0; i < nums.size(); i++) {
subMax += nums[i];
if (subMax > pre) {
pre = subMax;
}
if (subMax <= 0) { // 贪心的核心
subMax = 0;
}
}
return pre;
}
};
复杂度分析
- 时间复杂度:O(n),其中 n为数组nums的长度。
- 空间复杂度:O(1),只需常数空间存放变量。
方法二:动态规划
假设 nums 数组的长度是 n,下标从 0 到 n-1。
我们用 a(i) 代表以第 i 个数结尾的「连续子数组的最大和」,即用求最大值公式表达为:
a
(
i
)
=
m
a
x
a
(
i
)
0
≤
i
≤
n
−
1
a(i)=max {a(i)}_{0≤i≤n−1}
a(i)=maxa(i)0≤i≤n−1
我们需求出每个位置上的a(i),然后比较前一子序和是否大于0,当大于0时则当前子序和加上前一个子序和。从而我们需要比较
n
u
m
s
[
i
]
n
u
m
s
(
i
−
1
)
{nums}[i]nums(i−1)
nums[i]nums(i−1)和
n
u
m
s
[
i
]
nums[i]
nums[i]的大小,于是得到以下的动态规划转移方程:
a ( i ) = m a x { n u m s ( i − 1 ) + n u m s [ i ] , n u m s [ i ] } a(i)=max\\{{nums(i−1)+nums[i],nums[i]}\\} a(i)=max{nums(i−1)+nums[i],nums[i]}
python代码
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
subMax = 0
pre = nums[0]
for x in nums:
subMax = max(subMax + x, x)
pre = max(pre, subMax)
return pre
Java代码
class Solution {
public int maxSubArray(int[] nums) {
int subMax = 0;
int pre = nums[0];
for (int x : nums){
subMax = Math.max(subMax + x, x);
pre = Math.max(pre, subMax);
}
return pre;
}
}
C++代码
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre = 0, subMax = nums[0];
for (const auto &x: nums) {
pre = max(pre + x, x);
subMax = max(subMax, pre);
}
return maxAns;
}
};
复杂度分析
- 时间复杂度:O(n),其中 n为数组nums的长度。
- 空间复杂度:O(1),只需常数空间存放变量。
方法三:分治
我们现在需求区间[l, r]上的最大连续子序和mSum,按照分治算法的思路,应当通过划分[l, r]为左区间[l, m]和右区间R:[m+ 1, r]分治求解。
在一个区间 [l,r]内我们使用以下四个:
- lSum 表示 [l,r]内以 l 为左端点的最大子序和
- rSum 表示 [l,r] 内以 r 为右端点的最大子序和
- mSum 表示 [l,r] 内的最大子序和
- iSum 表示 [l,r]的区间和
如何通过左右子区间的信息合并得到 [l,r]的信息?
- 区间 [l,r]的iSum 就等于左子区间的iSum +右子区间的 iSum
- [l,r]的lSum,存在两种可能,它要么等于左子区间的lSum,要么等于左子区间的 iSum +右子区间的 lSum,二者取大。
- [l,r]的rSum,同理,它要么等于右子区间的 }rSum,要么等于右子区间的 iSum 加上左子区间的 rSum,二者取大。
- [l,r] 的 mSum 可能是左子区间的 mSum 也可能是 右子区间mSum还可能是左子区间的 rSum 和右子区间的lSum 的和,三者取大。
python代码
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
max_num = self.findMax(nums)
return max_num
def findMax(self, nums: List[int]) -> int:
if len(nums) == 1:
return nums[0]
else:
max_left = self.findMax(nums[0:len(nums) // 2])
max_right = self.findMax(nums[len(nums) // 2:len(nums)])
max_l = nums[len(nums) // 2 - 1]
count = 0
for i in range(len(nums) // 2 - 1, -1, -1):
count += nums[i]
max_l = max(count, max_l)
max_r = nums[len(nums) // 2]
count = 0
for i in range(len(nums) // 2, len(nums)):
count += nums[i]
max_r = max(count, max_r)
return max(max_right, max_left, max_l + max_r)
Java代码
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;
}
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;
Status lSub = getInfo(a, l, m);
Status rSub = getInfo(a, m + 1, r);
return pushUp(lSub, rSub);
}
public Status pushUp(Status l, Status r) {
int iSum = l.iSum + r.iSum;
int lSum = Math.max(l.lSum, l.iSum + r.lSum);
int rSum = Math.max(r.rSum, r.iSum + l.rSum);
int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
return new Status(lSum, rSum, mSum, iSum);
}
}
C++代码
class Solution {
public:
struct Status {
int lSum, rSum, mSum, iSum;
};
Status pushUp(Status l, Status r) {
int iSum = l.iSum + r.iSum;
int lSum = max(l.lSum, l.iSum + r.lSum);
int rSum = max(r.rSum, r.iSum + l.rSum);
int mSum = max(max(l.mSum, r.mSum), l.rSum + r.lSum);
return (Status) {lSum, rSum, mSum, iSum};
};
Status get(vector<int> &a, int l, int r) {
if (l == r) {
return (Status) {a[l], a[l], a[l], a[l]};
}
int m = (l + r) >> 1;
Status lSub = get(a, l, m);
Status rSub = get(a, m + 1, r);
return pushUp(lSub, rSub);
}
int maxSubArray(vector<int>& nums) {
return get(nums, 0, nums.size() - 1).mSum;
}
};
复杂度分析
- 时间复杂度:O( n l o g n nlogn nlogn),这里递归的深度是对数级别的,每一层需要遍历一遍数组。
- 空间复杂度:O( l o g n logn logn),需常数个变量,使用的空间取决于递归栈的深度。
以上是关于53. (Maximum Subarray)最大子序和的主要内容,如果未能解决你的问题,请参考以下文章
leetcode 53. Maximum Subarray 最大子数组和(中等)
53. Maximum Subarray(动态规划 求最大子数组)
[leetcode]53. Maximum Subarray最大子数组和
leetCode 53.Maximum Subarray (子数组的最大和) 解题思路方法