今天我们一起来看一下关于最大子数组的一些问题。最大子数组的应用场景可以是这样的:有一天,你搞了一场投资开始炒股,这时你就会想,我怎样才能获得最大的利润呢,最简单的想法就是我在股票的最低价时买入,然后在最高价时卖出,这样利润必然最大。但冷静下来想想这往往是不可能的,你不能保证最高价出现在最低价后面。为了达到这一目的,我们建立了最大子数组模型。我们以一年为时间期限,每个月的股价假设是这样的13 12 15 18 19 18 20 16 13 9 11 10,为了获得最大利益,我们要寻找差值最大的两个数(后一个减前一个)。为了简化模型,我们从另外一个角度来看待这个问题。当我们从数据的变化(也就是从后一个月相对于前一个月价格的变化量)角度来看待这个问题时,这一串数字就变成了这个样子0 -1 3 3 1 -1 2 -4 -3 -4 2 -1,这样问题就转化为寻找这个数组的最大非空连续子数组问题了。 下面我们就来讨论一下这个问题的解法:
解法一:暴力求解
最简单最直接的想法就是我求出所有非空连续子数组的和,再通过比较找到最大的一个子数组不就行了吗。确实可以,通过for循环遍历我们可以得到这样的伪代码:
暴力求解伪代码:
FindMaxSubarray(A,low,high)
1. maxSum = 负无穷大
2. sum = 0
3. for i = 1 to n
4. for j = i to n
5. sum=sum+A[j]
6. if sum > maxSum
7. maxSum = sum
8. maxLeft = i
9. maxRight = j
10. sum = 0
11. return (maxLeft,maxRight,maxSum)
伪代码讲解:
第一二行,maxSum用来表示当前扫描过的最大子数组的和,sum用来表示当前子数组的和
第三到十行,主体扫描所有子数组并求和,记录最大子数组的起始位置i、结束位置j以及和sum
第十一行返回扫描结果
C语言完整代码:
/*Author: Terry Zhang*/ #include <stdio.h> #include <stdlib.h> struct info{ int maxLeft; int maxRight; int Sum; }; int main() { size_t n; scanf_s("%d", &n); int *p = (int *)calloc(n, sizeof(int)); for (size_t i = 0; i < n; i++) { scanf_s("%d", p + i); } info Find_Max_Subarray(int A[], int low, int high); info inf; inf = Find_Max_Subarray(p, 0, n - 1); //输出结果 printf("MaxLeft:%d\n", inf.maxLeft+1); //默认起始位置为1 printf("MaxRight:%d\n", inf.maxRight+1); printf("MaxSum:%d\n", inf.Sum); if (p != NULL) free(p); return 0; } info Find_Max_Subarray(int A[], int low, int high) { int maxSum = INT_MIN; int maxLeft = 0; int maxRight = 0; int sum = 0; for (size_t i = low; i <= high; i++) { for (size_t j = i; j < high; j++) { sum += A[j]; if (sum >= maxSum) { maxSum = sum; maxLeft = i; maxRight = j; } } sum = 0; } info inf; inf.maxLeft = maxLeft; inf.maxRight = maxRight; inf.Sum = sum; return inf; }