Maximum Subarray
description:
Given an integer array nums
, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
这道题给出一个整数数组,让我们求出连续子数组中和最大的一个,这是一道经典的历史名题了。题目让我们试着用O(n)和分治的方法来做。
下面我来介绍此题的三种解法:
- solution1:
O(n)解法:O(n)线性时间复杂度的算法最先开始是在1977年被卡内基梅隆大学的教授Kadane发现并提出来的,因此也被称为Kadane’s algorithm。算法思想是:定义一个cur记录子数组的和,遍历数组,对于每一个数,去判断(之前子数组的和cur + 这个数num)和(这个数num)的大小,并让cur取它们中的较大者。也就是说只要前面的cur>0,就能使得子数组的和继续增大,cur=cur+num。否则cur=num,相当于前面的子数组和为负数,就重新选定num为新一轮cur的子数组元素,然后继续遍历。还要定义一个res,用来记录每次cur的最大值,这样把每次cur得到的结果取了一个最大值,也就是最终的结果了。
Mycode1:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
// Input: -2 1 -3 4 -1 2 1 -5 4
// Output: 6
//curSum记录当前前缀和的值, res记录每次前缀和的一个最大值
int res = INT_MIN, curSum = 0;
for(int num : nums){
//把当前前缀和和后一位的数字比较,如果当前前缀和<0(产生负影响),则会取num值
curSum = max(curSum + num, num);
//cout << curSum << ' ';
res = max(res, curSum);
}
return res;
}
};
- solution2: O(nlogn)
分治思想:有点类似二分,把一个大规模的要处理的问题划分成小的模块一个个去解决。算法思想:把题目所给的数组划分成两个数组,分别递归地去求左侧数组和右侧数组的最大子数组和。这样考虑出来的是左侧数组和右侧数组的maximum subarray,整个数组的最大子数组和还有可能经过中间的数,因此还有求一个由中间数出发的一个最大子数组和,然后把这三个最大值取一个max,就是答案了!
Mycode2:
class Solution {
public:
int find(vector<int>& nums, int left, int right){
//boundary
if (left == right) return nums[left];
if (left > right) return INT_MIN;
//declare
int mid = (left + right) / 2;
int lmax = 0, rmax = 0, ml = nums[mid], mr = 0;
//find
lmax = find(nums, left, mid - 1);
rmax = find(nums, mid + 1, right);
//find middle
//1. left
for(int i = mid, sum = 0; i >= left; i--){
sum += nums[i];
ml = max(ml, sum);
}
//2. right
for(int i = mid + 1, sum = 0; i <= right; i++){
sum += nums[i];
mr = max(mr, sum);
}
return max(max(lmax, rmax), ml + mr);
}
int maxSubArray(vector<int>& nums){
return find(nums, 0, nums.size() - 1);
}
};
- solution3:
也是O(n)的解法,和solution1有点相似,动态规划思想,但写法非常简单。算法思想是:把num数组打造成一个记录前缀子数组最大和的数组,nums[i]表示由0 ~ i的子数组最大和。于是nums[0]只能等于nums[0],循环由1 ~ len(nums)-1,如果前一个子数组最大和nums[i-1]<0,则能使的后面子数组和变大,于是nums[i] += nums[i-1],如果nums[i-1]<0,则只能使后面变小,则num[i]不变。最后在整个前缀子数组最大和的值里面取一个max就是答案了!
Mycode3:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
for i in range(1, len(nums)):
if (nums[i - 1] > 0):
nums[i] += nums[i - 1]
return max(nums)
O(n)的解法看着简单,理解起来不容易,要在草稿纸上多推算加深理解。
分治的解法博大精深,这种思想应用在很多经典算法里面,如快速排序,归并排序等等。算法之路道阻且长,吾将上下而求索!
谢谢你的观看!