连续子数组的最大和:最直观的想法是,使用一个变量i枚举区间起始位置,使用一个变量j枚举区间结束位置,使用一个变量k枚举区间变量值用于求和,使用一个变量sum用于求区间和,使用一个变量ans用于求区间和最大值,三层for循环。(但是很明显该方法超时故我们需要优化时间复杂度)
int FindGreatestSumOfSubArray(vector<int> array) { //数组长度 int n=array.size(); //最大和 int ans=INT_MIN; //起始位置 for(int i=0;i<n;i++) { //结束位置 for(int j=i;j<n;j++) { int sum=0; //区间和 for(int k=i;k<=j;k++) { sum+=array[k]; } ans=max(ans,sum); } } return ans; }
优化:上述方法存在一个问题,其重复计算了很多次区间和,我们可以使用前缀和优化,即区间[i,j]的区间和等于区间[0,j]的区间和减去区间[0,i-1]的区间和,故额外使用一个sum数组记录区间和。为了使得代码统一,可以使用sum[i+1]表示0~i区间和,其中sum[0]设为空集,即可统一递推式。(这样可以将三层for循环降为两层for循环但是还是超时故需要继续优化时间复杂度)
int FindGreatestSumOfSubArray(vector<int> array) { //数组长度 int n=array.size(); //区间和最大值 int ans=INT_MIN; //前缀和数组 sum[0]设为空集 sum[i+1]表示0~i区间和 vector<int> sum(n+1,0); for(int i=0;i<n;i++) sum[i+1]=sum[i]+array[i]; //起始位置 for(int i=0;i<n;i++) { //结束位置 for(int j=i;j<n;j++) { ans=max(ans,sum[j+1]-sum[i]); } } return ans; }
优化:上述两种方法均是枚举了起始位置和结束位置,但是实际上我们可以只枚举结束位置,不妨使用dp[i]表示以i为结尾的连续子数组的最大值,既然是以i为结尾,那么肯定是要包含i元素的。又由于其需要连续子数组,故如果dp[i-1]小于等于0,那么dp[i]就等于array[i],反之就等于dp[i-1]加上array[i]。最后使用一个变量res来记录最大值,即以哪一个元素为结尾的连续子数组和最大。
int FindGreatestSumOfSubArray(vector<int> array) { //dp数组 vector<int> dp(array.size(),0); dp[0]=array[0]; int res=dp[0]; for(int i=1;i<array.size();i++) { dp[i]=max(dp[i-1]+array[i],array[i]); res=max(res,dp[i]); } return res; }
优化:上述动态规划方法,我们发现每次最多只会使用上一个状态,故我们不需要使用dp数组进行存储,直接使用一个int变量存储即可。
int FindGreatestSumOfSubArray(vector<int> array) { int sum=array[0]; int res=array[0]; for(int i=1;i<array.size();i++) { sum=max(sum+array[i],array[i]); res=max(res,sum); } return res; }
PS:最开始刷题一题一解就可以,但是刷多了后可以尝试变换思维,想想一题多解,即最直观的最暴力的想法是什么,然后接着想想有哪些缺点即有哪些优化空间。