问题
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。
例如输入的数组为-2,11,-4,13,-5,-2 和最大的子数组为11,-4,13
因此输出为该子数组的和20。
解析:
本题解法多种多样,时间复杂度可以为:O(n^3),O(n^2),O(nlogn),O(n);
我重点介绍一下时间复杂度较低的两种方法
方法1
- /*
- 时间复杂度O(logn)使用分治思想
- 最大子连续数组可能出现在左部分,右部分,或者中间区域
- */
- int methord3(int* pArr, int strat, int end)
- {
- if ( strat > end )
- return 0;
- if ( strat == end )
- {
- return pArr[strat];
- }
- int mid = strat + ((end-strat)>>1);
- int lsumborder = pArr[mid], lsum = 0;
- for ( int i = mid; i >= strat; i-- )
- {
- lsum += pArr[i];
- lsumborder = max(lsumborder,lsum);
- }
- int rsumborder = pArr[mid+1], rsum = 0;
- for ( int i = mid+1; i <= end; i++ )
- {
- rsum += pArr[i];
- rsumborder = max(rsumborder,rsum);
- }
- int lmaxsum = methord3(pArr,strat,mid);//左部分的最大和
- int rmaxsum = methord3(pArr,mid+1,end);//右部分的最大和
- return max(max(lmaxsum,rmaxsum),lsumborder+rsumborder); //判断3者中最大的作为返回值
- }
方法2
- /*
- 本方法采用 动态规划 的思想,时间复杂度为O(n),空间复杂度为O(1)
- 假设,我们已经前i个元素的最大子数组
- */
- int methord4(int* pArr, int start, int end)
- {
- if ( start > end )
- {
- return 0;
- }
- if ( start == end )
- {
- return pArr[start];
- }
- int nStart = pArr[start]; //求每一段的子数组的和
- int nAll = pArr[start]; //用于记录当前的子数组和的最大值
- for ( int i = start+1; i <= end; i++ )
- {
- nStart = max(pArr[i],nStart+pArr[i]);
- nAll = max(nStart,nAll);
- }
- return nAll;
- }
pArr[] | -2 | 11 | -4 | 13 | -5 | -2 |
nSart | -2 | 11 | 7 | 20 | 15 | 13 |
nAll | -2 | 11 | 11 | 20 | 20 | 20 |
拓展:
根据上面的解法,我们很容易会做,求连续子数组乘积的最大值了。如上述例子,那么最大子数组乘积应该是11440。
思路:
假设数组为a[],直接利用动态规划求解,考虑到可能存在负数的情况,我们用maxProduct来表示以当前循环位置i结尾的最大连续子串的乘积值,用minProduct表示以当前循环位置i结尾的最小的子串的乘积值,
那么状态转移方程为:
maxProduct=max{a[i+1], maxProduct*a[i+1], minProduct*a[i+1]};
minProduct=min{a[i+1], maxProduct*a[i+1], minProduct*a[i+1]};
初始状态为 minProduct = maxProduct = endProduct = a[0];
这样代码很容易写出来了
- int MaxProduct(int* pArr, int start, int end)
- {
- if ( start > end )
- return 0;
- if ( start == end )
- return pArr[start];
- int maxProduct,minProduct,endProduct;
- minProduct = maxProduct = endProduct = pArr[start];
- for ( int i = start+1; i <= end; i++ )
- {
- int a = pArr[i]*maxProduct;
- int b = pArr[i]*minProduct; //先把原最大值和最小值分别乘以pArr[i]记录下来,在进行比较
- maxProduct = max( max(pArr[i],a), b);
- minProduct = min( min(pArr[i],a), b);
- endProduct = max( max(endProduct,maxProduct),minProduct );
- }
- return endProduct;
- }
原文地址:点击打开链接