53.给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
- 看到这个题最初的想法是分别计算所有长度的子序列的和,然后取最大的那一个,于是有了以下的代码,显然这是一种暴力解法,时间复杂度太高了,在leetcode过不了。
int calSum(int* nums, int start, int end) {
int sum = 0;
for (int i = start; i <= end; i++) {
sum += nums[i];
}
return sum;
}
int maxSubArray(int* nums, int numsSize){
int maxSum = INT_MIN;
for (int len = 1; len <= numsSize; len++) {
for (int i = 0; i <= numsSize - len; i++) {
int sum = calSum(nums, i, i + len - 1);
if (sum > maxSum) {
maxSum = sum;
}
}
}
return maxSum;
}
- 暴力解法的第二种写法是这样的,它与第一种写法的差别在于,第一种写法在每次选定一个起点的时候,只选找一个子序列然后就改变起点了,这样其实是写得过于复杂了,而第二种写法是在每次选定一个起点后,就找所有的以该位置为起点的子序列,会省去很多第一种写法多出来的步骤。
int maxSubArray(int* nums, int numsSize){
int maxSum = INT_MIN;
int count = 0;
for (int i = 0; i < numsSize; i++) { //选择某个位置作为子序列起点
count = 0;
for (int j = i; j < numsSize; j++) { // 计算以该位置作为起点的子序列和最大的子序列
count += nums[j];
maxSum = count > maxSum ? count : maxSum;
}
}
return maxSum;
}
- 但暴力解法并不是我们重点要关注的。我们重点要关注的是如何利用贪心算法来解题,贪心算法在该题表现在哪里?我们找一个子序列需要确定一个起点,如果序列中有正数的话,那么我们选择的起点对应的数值应该是正数,如果起点对应的数值是负数的话,那么该子序列就不可能是和最大的子序列,这就是贪心所在:选择的子序列起点的符号要尽可能是正的,并且在寻找子序列的过程中,若当前子序列和为负数,则要放弃当前子序列,从下一个数开始重新计算子序列。
int maxSubArray(int* nums, int numsSize){
int maxSum = INT_MIN; //用来记录当前出现的最大的子序列和
int count = 0; //用来累计子序列的和
for (int i = 0; i < numsSize; i++) {
count += nums[i];
if (count >= maxSum)
maxSum = count;
if (count <= 0) //如果当前子序列和为负数的话,则以下一个元素作为起点重新计算子序列
count = 0;
}
return maxSum;
}