1 题目
题目:最大子数组(Maximum Subarray)
描述:**给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。每个子数组的数字在数组中的位置应该是连续的。子数组最少包含一个数 **
lintcode题号——41,难度——easy
样例1:
输入:nums = [−2,2,−3,4,−1,2,1,−5,3]
输出:6
解释:符合要求的子数组为[4,−1,2,1],其最大和为 6。
样例2:
输入:nums = [1,2,3,4]
输出:10
解释:符合要求的子数组为[1,2,3,4],其最大和为 10。
2 解决方案
2.1 思路
常规思路可以采用暴力法,使用两个序号,两重循环遍历出所有子数组来找到最大的,时间复杂度为O(^2)。
这题可以只使用O(n)的复杂度来解决,引入一个前缀和
的概念,即从头部开始到该位置的前一位置的数值累加和,最小前缀和
即为之前所有位置的前缀和
中最小的那个,要找到最大的子数组的值,只要通过当前位置的累加值减去之前的最小前缀和
即可,在一次循环内即可完成累加求和以及更新最小前缀和
的操作。
2.3 时间复杂度
时间复杂度为O(n)。
2.4 空间复杂度
空间复杂度为O(1)。
3 源码
细节:
- 一边求当前累加和,一边求当前位置往前的最小前缀和,以当前位置结尾的最大子数组的值即为两者之差。
C++版本:
/**
* @param nums: A list of integers
* @return: A integer indicate the sum of max subarray
*/
int maxSubArray(vector<int> &nums) {
// write your code here
if (nums.empty())
{
return 0;
}
int maxSum = INT_MIN;
int sum = 0; // 当前累加和
int minPrefixSum = 0; // 最小的前缀和,不用记录下标
for (int i = 0; i < nums.size(); i++)
{
sum += nums.at(i);
maxSum = max(sum - minPrefixSum, maxSum); // 累加和-最小前缀和=子数组可能的最大值
minPrefixSum = min(sum, minPrefixSum); // 更新最小前缀和
}
return maxSum;
}
4 另解——贪心算法
细节:
- 用贪心的思想也可以解,思路是一旦发现前面的子串为负,无法为整个串带来正收益,则抛弃该子串,重新开始计算。
C++版本:
/**
* @param nums: A list of integers
* @return: A integer indicate the sum of max subarray
*/
int maxSubArray(vector<int> &nums) {
// write your code here
if (nums.empty())
{
return 0;
}
int result = INT_MIN;
int curSum = 0;
for (int i = 0; i < nums.size(); i++)
{
curSum += nums.at(i);
result = max(curSum, result);
curSum = max(curSum, 0); // 子串只取大于0的
}
return result;
}