求数组的子数组之和的最大值
明确题意:
- 子数组是连续的
- 只需要求和,不需要返回子数组的具体位置
- 元素是整数,数组可能包含正整数、零、负整数
举例子:
数组 [1, -2, 3, 5, -3, 2] 应返回: 8
明确题意
起初我想的是即使是循环,也不能解决这个问题,比如以上例子中元素3,5组成“最大”子数组。更何况如果3和5距离很远。当然后者加剧了题目的复杂度。
所以,第一步,明确“题”意是求解很重要的一环。
示例代码1:
int maxSum(int* A, int n) { int maximum = -1; int sum; for (int i = 0; i < n; ++i){ sum = 0; for (int j = i; j < n; ++j){ sum += A[j]; if (sum > maximum){ maximum = sum; } } } return maximum; }
但是通过以上O(n^2)复杂度的算法,确实得出题目的解。
实际上这里面是隐含着我所考虑的情况,而且是全部。所要求的是一个连续的子数组的和。那么所有的情况可以分类:
针对1个元素,(a)组成的数组,那么肯定只能是它本身;
针对2个元素,(a,b)组成的数组,除了第一种情况之外,那么就是唯一的一个组合,(第一个元素,第二个元素),即(a,b)
针对3个元素,(a,b,c)组成的数组,也可以罗列出来,如:a或b或c,组合有(a,b) 或 (b,c)(其中ac不满足连续的要求)
针对4个元素,(以此类推)
以上是一个类似小学数学教育当中经常用到的不完全归纳法,通过这种分析,可以得出一种结果,就是,最大子数组的可以按一种标准分类,那就是子数组元素的个数。再加上一个额外的限制条件连续,就可以使用两个循环来解决问题。对于n个元素组成的数组:
(1,2,3 ,... ,n)
设连续的子数组情况有m种(数学不好,凑合着表示):
m = cn1 + (cn2 - 不连续的) + (cn3 - 不连续的) + ... + (cnn-不连续的)
回过头来再看 示例代码1,内外两层循环上来看,的确是覆盖了以上所有的组合情况。
以数组下表来表示:
maximum来保存最大的子数组和,sum保存某一种子数组的元素组合情况。以下分别用数组元素下标来表示元素的值: 当i=0的时候,sum分别来表示 0 , 0+1, (0+1)+2, (0+1+2)+ 3 , ... , (0+1+2+3+ ... + n-1) + n 当i=1的时候,sum分别来表示 1, 1+2, (1+2)+ 3 , ... , (1+2+3+ ... + n-1) + n
... 当i=n-1的时候,sum分别来表示 n-1, n-1 + n 当i=n的时候,sum分别来表示 n
以上的组合均是连续的。
对于文章开头的例子,最大子数组(3,5)中元素在原来数组中的下表分别是(2,3),即当n=5,i=2,j=3的时候便得到了最大值8
在合适的地方打上适当的输出语句用以观察整个运算情况:
i j sum maximum 0 0 1 1 0 1 -1 1 0 2 2 2 0 3 7 7 0 4 4 7 0 5 6 7 1 1 -2 7 1 2 1 7 1 3 6 7 1 4 3 7 1 5 5 7 2 2 3 7 2 3 8 8 2 4 5 8 2 5 7 8 3 3 5 8 3 4 2 8 3 5 4 8 4 4 -3 8 4 5 -1 8 5 5 2 8
分治
每个问题都可以分解成为两个问题规模减半的子问题,再加上一次遍历算法。
int max(int x, int y){ return (x > y) ? x : y; } int maxSum_demo2(int* A,int n){ int nStart = A[n-1]; int nAll = A[n-1]; for (int i = n-2; i >= 0; --i) { nStart = max(A[i], nStart + A[i]); nAll = max(nStart, nAll); } return nAll; }
同样地,在合适的地方打印出结果:
i A[i] nStart nAll 4 -3 -1 2 3 5 5 5 2 3 8 8 1 -2 6 8 0 1 7 8
动态规划,分治待看。