[Lc]53最大子序和
Contents
题目
题解
这题有三种解法,其中第一种暴力法肯定超时,因此主要讨论后两种方法
1. 暴力法
这个方法就是取每种情况并计算当前情况的子序和,时间复杂度达到立方,肯定超时
- 时间复杂度$O(n^(3))$
- 空间复杂度$O(1)$
2. 动态规划法
这个方法是时间复杂度最优也最好理解最好写的方法
思路就是存两个状态,具体看注释吧
其实这道题主要就是有负数会减小子序和
- 时间复杂度$O(n)$
- 空间复杂度$O(1)$
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//动态规划法,主要维护两个个数,cur_sum(包括该元素的最大子序和),max_sum(当前存在的最大子序和)。
//每一步选择最优方案,到全局就是全局最优方案
int n = nums.size();
int cur_sum = nums[0];
int max_sum = nums[0];
for(int i =1;i<n;i++){
cur_sum = max(nums[i],cur_sum+nums[i]);
max_sum = max(max_sum,cur_sum);
}
return max_sum;
}
};
3. 分治法
这道题其实不适合用分治,因为除了左右两边还需要算跨过中间值的子序和,还需要用到递归增加空间复杂度,还难写
但是题目要求用分治,那就用吧
- 时间复杂度$O(n)$
- 空间复杂度$O(log{n})$
class Solution {//分治法
public:
int divide(vector<int>& nums,int left, int right){
if(left==right){
return nums[left];
}//左右指针重合直接返回
int center = left+(right-left)/2;;//取中间值
//下面递归左右两边,分治思想
//最后一行计算包括中间的最大子序和
int left_sum = divide(nums,left,center);
int right_sum = divide(nums,center+1,right);//注意center要+1,只算右边
int cross_sum = cross_max_sum(nums,left,right,center);
//计算上述三种分块的最大值
int max_LR = max(left_sum,right_sum);
return max(max_LR,cross_sum);
}
int cross_max_sum(vector<int>& nums,int left,int right,int center){
if(left==right){
return nums[left];
}//左右指针重合,直接返回
//初始化四个数,左边最大值,右边最大值,左边当前和,右边当前和
//上述四个数均包含中间数
int left_max_sum = INT_MIN;
int right_max_sum = INT_MIN;
int left_cur_sum = 0;
int right_cur_sum = 0;
//向左右扩展计算左右的最大值,然后将左右加起来
for(int i=center;i>=left;--i){
left_cur_sum += nums[i];
left_max_sum = max(left_max_sum,left_cur_sum);
}
for(int i=center+1;i<=right;++i){//注意center+1,避免重复
right_cur_sum += nums[i];
right_max_sum = max(right_max_sum,right_cur_sum);
}
//cout<<left_max_sum<<' '<<right_max_sum<<endl;
return left_max_sum+right_max_sum;
}
int maxSubArray(vector<int>& nums) {
return divide(nums,0,nums.size()-1);
}
};
Author ChrisHRZ
LastMod 2020-06-06