给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
方法一:动态规划
代码:
1 class Solution { 2 public: 3 int maxSubArray(vector<int>& nums) { 4 int m=nums[0],cur=nums[0]; 5 for(int i=1;i<nums.size();++i){ 6 cur=cur<0?nums[i]:cur+nums[i]; 7 m=max(m,cur); 8 } 9 return m; 10 } 11 };
方法二:线段树
代码:
1 class Solution { 2 public: 3 /** 4 *线段树:分治,将一段序列分成左右两段来求解。 5 *lSum 表示 [l, r] 内以 l 为左端点的最大子段和。lSum=max(左段.lSum,左段.iSum+右段.lSum) 6 *rSum 表示 [l, r] 内以 r 为右端点的最大子段和。rSum=max(右段.rSum,右段.iSum+左段.rSum) 7 *iSum 表示 [l, r] 的区间和。iSum=左段.iSum+右段.iSum 8 *mSum 表示 [l, r] 内的最大子段和。分三种情况,最大子序在左段中,在右段中,跨段。mSum=max(l.mSum,r.mSum,l.rSum+r.lSum) 9 */ 10 /*与动态规划相比,时间复杂度相同,但是因为使用了递归,并且维护了四个信息的结构体,运行的时间略长,空间复杂度也不如动态规划优秀,
11 而且难以理解。那么这种方法存在的意义是什么呢?对于这道题而言,确实是如此的。但是仔细观察分治法,它不仅可以解决区间 [0, n-1],还可以用于
12 解决任意的子区间 [l,r] 的问题。如果我们把 [0,n−1] 分治下去出现的所有子区间的信息都用堆式存储的方式记忆化下来,即建成一颗真正的树之后,
13 我们就可以在O(logn) 的时间内求到任意区间内的答案,我们甚至可以修改序列中的值,做一些简单的维护,之后仍然可以在O(logn) 的时间内求到任意
14 区间内的答案,对于大规模查询的情况下,这种方法的优势便体现了出来。这棵树就是神奇的数据结构——线段树。*/ 15 struct Status{ 16 int lSum,rSum,iSum,mSum; 17 }; 18 Status pushUp(Status l,Status r){ 19 int lSum=max(l.lSum,l.iSum+r.lSum); 20 int rSum=max(r.rSum,r.iSum+l.rSum); 21 int iSum=l.iSum+r.iSum; 22 int mSum=max(max(l.mSum,r.mSum),l.rSum+r.lSum); 23 return (Status){lSum,rSum,iSum,mSum}; 24 } 25 Status get(vector<int>& nums,int l,int r){ 26 if(l==r) 27 return Status{nums[l],nums[l],nums[l],nums[l]}; 28 int m=(l+r)>>1; 29 return pushUp(get(nums,l,m),get(nums,m+1,r)); 30 } 31 int maxSubArray(vector<int>& nums) { 32 return get(nums,0,nums.size()-1).mSum; 33 } 34 };