leetcode-master/problems/0053.最大子序和.md

218 lines
7.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 53. 最大子序和
题目地址https://leetcode-cn.com/problems/maximum-subarray/
给定一个整数数组 nums 找到一个具有最大和的连续子数组子数组最少包含一个元素返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大 6。
## 暴力解法
暴力解法的思路第一层for 就是设置起始位置第二层for循环遍历数组寻找最大值
时间复杂度O(n^2)
空间复杂度O(1)
```C++
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int result = INT32_MIN;
int count = 0;
for (int i = 0; i < nums.size(); i++) { // 设置起始位置
count = 0;
for (int j = i; j < nums.size(); j++) { // 每次从起始位置i开始遍历寻找最大值
count += nums[j];
result = count > result ? count : result;
}
}
return result;
}
};
```
以上暴力的解法C++勉强可以过,其他语言就不确定了。
## 贪心解法
**贪心贪的是哪里呢?**
如果 -2 1 在一起计算起点的时候一定是从1开始计算因为负数只会拉低总和这就是贪心贪的地方
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”
**局部最优的情况下,并记录最大的“连续和”,可以推出全局最优**。
从代码角度上来讲遍历nums从头开始用count累积如果count一旦加上nums[i]变为负数那么就应该从nums[i+1]开始从0累积count了因为已经变为负数的count只会拖累总和。
**这相当于是暴力解法中的不断调整最大子序和区间的起始位置**。
**那有同学问了,区间终止位置不用调整么? 如何才能得到最大“连续和”呢?**
区间的终止位置其实就是如果count取到最大值了及时记录下来了。例如如下代码
```
if (count > result) result = count;
```
**这样相当于是用result记录最大子序和区间和变相的算是调整了终止位置**。
如动画所示:
![53.最大子序和](https://code-thinking.cdn.bcebos.com/gifs/53.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C.gif)
红色的起始位置就是贪心每次取count为正数的时候开始一个区间的统计。
那么不难写出如下C++代码(关键地方已经注释)
```C++
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int result = INT32_MIN;
int count = 0;
for (int i = 0; i < nums.size(); i++) {
count += nums[i];
if (count > result) { // 取区间累计的最大值(相当于不断确定最大子序终止位置)
result = count;
}
if (count <= 0) count = 0; // 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
}
return result;
}
};
```
时间复杂度O(n)
空间复杂度O(1)
当然题目没有说如果数组为空,应该返回什么,所以数组为空的话返回啥都可以了。
## 动态规划
当然本题还可以用动态规划来做,当前[「代码随想录」](https://img-blog.csdnimg.cn/20201124161234338.png)主要讲解贪心系列后续到动态规划系列的时候会详细讲解本题的dp方法。
那么先给出我的dp代码如下有时间的录友可以提前做一做
```C++
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if (nums.size() == 0) return 0;
vector<int> dp(nums.size(), 0); // dp[i]表示包括i之前的最大连续子序列和
dp[0] = nums[0];
int result = dp[0];
for (int i = 1; i < nums.size(); i++) {
dp[i] = max(dp[i - 1] + nums[i], nums[i]); // 状态转移公式
if (dp[i] > result) result = dp[i]; // result 保存dp[i]的最大值
}
return result;
}
};
```
时间复杂度O(n)
空间复杂度O(n)
## 总结
本题的贪心思路其实并不好想,这也进一步验证了,别看贪心理论很直白,有时候看似是常识,但贪心的题目一点都不简单!
后续将介绍的贪心题目都挺难的,哈哈,所以贪心很有意思,别小看贪心!
## 其他语言版本
Java
```java
class Solution {
public int maxSubArray(int[] nums) {
if (nums.length == 1){
return nums[0];
}
int sum = Integer.MIN_VALUE;
int count = 0;
for (int i = 0; i < nums.length; i++){
count += nums[i];
sum = Math.max(sum, count); // 取区间累计的最大值(相当于不断确定最大子序终止位置)
if (count <= 0){
count = 0; // 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
}
}
return sum;
}
}
```
Python
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
result = -float('inf')
count = 0
for i in range(len(nums)):
count += nums[i]
if count > result:
result = count
if count <= 0:
count = 0
return result
```
Go
```go
func maxSubArray(nums []int) int {
maxSum := nums[0]
for i := 1; i < len(nums); i++ {
if nums[i] + nums[i-1] > nums[i] {
nums[i] += nums[i-1]
}
if nums[i] > maxSum {
maxSum = nums[i]
}
}
return maxSum
}
```
Javascript:
```Javascript
var maxSubArray = function(nums) {
let result = -Infinity
let count = 0
for(let i = 0; i < nums.length; i++) {
count += nums[i]
if(count > result) {
result = count
}
if(count < 0) {
count = 0
}
}
return result
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>