mirror of https://github.com/doocs/leetcode.git
286 lines
6.3 KiB
Markdown
286 lines
6.3 KiB
Markdown
---
|
||
comments: true
|
||
difficulty: 中等
|
||
edit_url: https://github.com/doocs/leetcode/edit/main/solution/0000-0099/0053.Maximum%20Subarray/README.md
|
||
tags:
|
||
- 数组
|
||
- 分治
|
||
- 动态规划
|
||
---
|
||
|
||
<!-- problem:start -->
|
||
|
||
# [53. 最大子数组和](https://leetcode.cn/problems/maximum-subarray)
|
||
|
||
[English Version](/solution/0000-0099/0053.Maximum%20Subarray/README_EN.md)
|
||
|
||
## 题目描述
|
||
|
||
<!-- description:start -->
|
||
|
||
<p>给你一个整数数组 <code>nums</code> ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。</p>
|
||
|
||
<p><strong><span data-keyword="subarray-nonempty">子数组 </span></strong>是数组中的一个连续部分。</p>
|
||
|
||
<p> </p>
|
||
|
||
<p><strong>示例 1:</strong></p>
|
||
|
||
<pre>
|
||
<strong>输入:</strong>nums = [-2,1,-3,4,-1,2,1,-5,4]
|
||
<strong>输出:</strong>6
|
||
<strong>解释:</strong>连续子数组 [4,-1,2,1] 的和最大,为 6 。
|
||
</pre>
|
||
|
||
<p><strong>示例 2:</strong></p>
|
||
|
||
<pre>
|
||
<strong>输入:</strong>nums = [1]
|
||
<strong>输出:</strong>1
|
||
</pre>
|
||
|
||
<p><strong>示例 3:</strong></p>
|
||
|
||
<pre>
|
||
<strong>输入:</strong>nums = [5,4,-1,7,8]
|
||
<strong>输出:</strong>23
|
||
</pre>
|
||
|
||
<p> </p>
|
||
|
||
<p><strong>提示:</strong></p>
|
||
|
||
<ul>
|
||
<li><code>1 <= nums.length <= 10<sup>5</sup></code></li>
|
||
<li><code>-10<sup>4</sup> <= nums[i] <= 10<sup>4</sup></code></li>
|
||
</ul>
|
||
|
||
<p> </p>
|
||
|
||
<p><strong>进阶:</strong>如果你已经实现复杂度为 <code>O(n)</code> 的解法,尝试使用更为精妙的 <strong>分治法</strong> 求解。</p>
|
||
|
||
<!-- description:end -->
|
||
|
||
## 解法
|
||
|
||
<!-- solution:start -->
|
||
|
||
### 方法一:动态规划
|
||
|
||
我们定义 $f[i]$ 表示以元素 $\textit{nums}[i]$ 为结尾的连续子数组的最大和,初始时 $f[0] = \textit{nums}[0]$,那么最终我们要求的答案即为 $\max_{0 \leq i < n} f[i]$。
|
||
|
||
考虑 $f[i]$,其中 $i \geq 1$,它的状态转移方程为:
|
||
|
||
$$
|
||
f[i] = \max(f[i - 1] + \textit{nums}[i], \textit{nums}[i])
|
||
$$
|
||
|
||
也即:
|
||
|
||
$$
|
||
f[i] = \max(f[i - 1], 0) + \textit{nums}[i]
|
||
$$
|
||
|
||
由于 $f[i]$ 只与 $f[i - 1]$ 有关系,因此我们可以只用一个变量 $f$ 来维护对于当前 $f[i]$ 的值是多少,然后进行状态转移即可。答案为 $\max_{0 \leq i < n} f$。
|
||
|
||
时间复杂度 $O(n)$,其中 $n$ 为数组 $\textit{nums}$ 的长度。空间复杂度 $O(1)$。
|
||
|
||
<!-- tabs:start -->
|
||
|
||
#### Python3
|
||
|
||
```python
|
||
class Solution:
|
||
def maxSubArray(self, nums: List[int]) -> int:
|
||
ans = f = nums[0]
|
||
for x in nums[1:]:
|
||
f = max(f, 0) + x
|
||
ans = max(ans, f)
|
||
return ans
|
||
```
|
||
|
||
#### Java
|
||
|
||
```java
|
||
class Solution {
|
||
public int maxSubArray(int[] nums) {
|
||
int ans = nums[0];
|
||
for (int i = 1, f = nums[0]; i < nums.length; ++i) {
|
||
f = Math.max(f, 0) + nums[i];
|
||
ans = Math.max(ans, f);
|
||
}
|
||
return ans;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### C++
|
||
|
||
```cpp
|
||
class Solution {
|
||
public:
|
||
int maxSubArray(vector<int>& nums) {
|
||
int ans = nums[0], f = nums[0];
|
||
for (int i = 1; i < nums.size(); ++i) {
|
||
f = max(f, 0) + nums[i];
|
||
ans = max(ans, f);
|
||
}
|
||
return ans;
|
||
}
|
||
};
|
||
```
|
||
|
||
#### Go
|
||
|
||
```go
|
||
func maxSubArray(nums []int) int {
|
||
ans, f := nums[0], nums[0]
|
||
for _, x := range nums[1:] {
|
||
f = max(f, 0) + x
|
||
ans = max(ans, f)
|
||
}
|
||
return ans
|
||
}
|
||
```
|
||
|
||
#### TypeScript
|
||
|
||
```ts
|
||
function maxSubArray(nums: number[]): number {
|
||
let [ans, f] = [nums[0], nums[0]];
|
||
for (let i = 1; i < nums.length; ++i) {
|
||
f = Math.max(f, 0) + nums[i];
|
||
ans = Math.max(ans, f);
|
||
}
|
||
return ans;
|
||
}
|
||
```
|
||
|
||
#### Rust
|
||
|
||
```rust
|
||
impl Solution {
|
||
pub fn max_sub_array(nums: Vec<i32>) -> i32 {
|
||
let n = nums.len();
|
||
let mut ans = nums[0];
|
||
let mut f = nums[0];
|
||
for i in 1..n {
|
||
f = f.max(0) + nums[i];
|
||
ans = ans.max(f);
|
||
}
|
||
ans
|
||
}
|
||
}
|
||
```
|
||
|
||
#### JavaScript
|
||
|
||
```js
|
||
/**
|
||
* @param {number[]} nums
|
||
* @return {number}
|
||
*/
|
||
var maxSubArray = function (nums) {
|
||
let [ans, f] = [nums[0], nums[0]];
|
||
for (let i = 1; i < nums.length; ++i) {
|
||
f = Math.max(f, 0) + nums[i];
|
||
ans = Math.max(ans, f);
|
||
}
|
||
return ans;
|
||
};
|
||
```
|
||
|
||
#### C#
|
||
|
||
```cs
|
||
public class Solution {
|
||
public int MaxSubArray(int[] nums) {
|
||
int ans = nums[0], f = nums[0];
|
||
for (int i = 1; i < nums.Length; ++i) {
|
||
f = Math.Max(f, 0) + nums[i];
|
||
ans = Math.Max(ans, f);
|
||
}
|
||
return ans;
|
||
}
|
||
}
|
||
```
|
||
|
||
<!-- tabs:end -->
|
||
|
||
<!-- solution:end -->
|
||
|
||
<!-- solution:start -->
|
||
|
||
### 方法二
|
||
|
||
<!-- tabs:start -->
|
||
|
||
#### Python3
|
||
|
||
```python
|
||
class Solution:
|
||
def maxSubArray(self, nums: List[int]) -> int:
|
||
def crossMaxSub(nums, left, mid, right):
|
||
lsum = rsum = 0
|
||
lmx = rmx = -inf
|
||
for i in range(mid, left - 1, -1):
|
||
lsum += nums[i]
|
||
lmx = max(lmx, lsum)
|
||
for i in range(mid + 1, right + 1):
|
||
rsum += nums[i]
|
||
rmx = max(rmx, rsum)
|
||
return lmx + rmx
|
||
|
||
def maxSub(nums, left, right):
|
||
if left == right:
|
||
return nums[left]
|
||
mid = (left + right) >> 1
|
||
lsum = maxSub(nums, left, mid)
|
||
rsum = maxSub(nums, mid + 1, right)
|
||
csum = crossMaxSub(nums, left, mid, right)
|
||
return max(lsum, rsum, csum)
|
||
|
||
left, right = 0, len(nums) - 1
|
||
return maxSub(nums, left, right)
|
||
```
|
||
|
||
#### Java
|
||
|
||
```java
|
||
class Solution {
|
||
public int maxSubArray(int[] nums) {
|
||
return maxSub(nums, 0, nums.length - 1);
|
||
}
|
||
|
||
private int maxSub(int[] nums, int left, int right) {
|
||
if (left == right) {
|
||
return nums[left];
|
||
}
|
||
int mid = (left + right) >>> 1;
|
||
int lsum = maxSub(nums, left, mid);
|
||
int rsum = maxSub(nums, mid + 1, right);
|
||
return Math.max(Math.max(lsum, rsum), crossMaxSub(nums, left, mid, right));
|
||
}
|
||
|
||
private int crossMaxSub(int[] nums, int left, int mid, int right) {
|
||
int lsum = 0, rsum = 0;
|
||
int lmx = Integer.MIN_VALUE, rmx = Integer.MIN_VALUE;
|
||
for (int i = mid; i >= left; --i) {
|
||
lsum += nums[i];
|
||
lmx = Math.max(lmx, lsum);
|
||
}
|
||
for (int i = mid + 1; i <= right; ++i) {
|
||
rsum += nums[i];
|
||
rmx = Math.max(rmx, rsum);
|
||
}
|
||
return lmx + rmx;
|
||
}
|
||
}
|
||
```
|
||
|
||
<!-- tabs:end -->
|
||
|
||
<!-- solution:end -->
|
||
|
||
<!-- problem:end -->
|