leetcode/solution/0000-0099/0053.Maximum Subarray/README.md

286 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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>&nbsp;</p>
<p><strong>示例 1</strong></p>
<pre>
<strong>输入:</strong>nums = [-2,1,-3,4,-1,2,1,-5,4]
<strong>输出:</strong>6
<strong>解释:</strong>连续子数组&nbsp;[4,-1,2,1] 的和最大,为&nbsp;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>&nbsp;</p>
<p><strong>提示:</strong></p>
<ul>
<li><code>1 &lt;= nums.length &lt;= 10<sup>5</sup></code></li>
<li><code>-10<sup>4</sup> &lt;= nums[i] &lt;= 10<sup>4</sup></code></li>
</ul>
<p>&nbsp;</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 -->