上一篇我们实现两种算法解决选择问题,这一章可以看到一个更经典的例子:最大子序列和问题。它有多种算法解决,而且效率差异非常的大。
下面我们来看问题,以及各个算法的具体实现,感受一下各个算法差异所在。
最大子序列和问题
问题描述: 给定整数 A1, A2, … , AN (可能有负数), 求 ∑jk=iAk 的最大值(方便起见,如果所有整数都为负数,则最大子序列和为 0 )
例:
输入 -2, 11, -4, 13, -5, -1 时, 答案为 20 (从 A2 到 A4)
下面给出时间复杂度分别为 O3, O2, O1 的算法实现,附带测试程序:
#include <stdio.h>
#include <stdlib.h>
int maxsubSumO3(const int array[], int n) {
int i, j, k;
int thisSum, maxSum = 0;
for (i = 0; i < n; i++) {
for (j = i; j < n; j ++) {
thisSum = 0;
for (k = i; k < j; k++)
thisSum += array[k];
if (thisSum > maxSum)
maxSum = thisSum;
}
}
return maxSum;
}
int maxsubSumO2(const int array[], int n) {
int i, j;
int thisSum, maxSum = 0;
for (i = 0; i < n; i++) {
thisSum = 0;
for (j = i; j < n; j++) {
thisSum += array[j];
if (thisSum > maxSum)
maxSum = thisSum;
}
}
return maxSum;
}
int maxsubSumO1(int const array[], int n) {
int j;
int thisSum = 0, maxSum = 0;
for (j = 0; j < n; j++) {
thisSum += array[j];
if (thisSum > maxSum)
maxSum = thisSum;
if (thisSum < 0)
thisSum = 0;
}
return maxSum;
}
int main() {
int array[10];
for (int i = 0; i < 10; i++) {
array[i] = ((double)rand()/RAND_MAX - 0.5) * 10;
printf("num %d: %d\n", i, array[i]);
}
printf("O3 algrthorm result is :%d\n", maxsubSumO3(array, 10));
printf("O2 algrthorm result is :%d\n", maxsubSumO2(array, 10));
printf("O1 algrthorm result is :%d\n", maxsubSumO1(array, 10));
}
输出结果如下图所示:
注意
对于线性时间O1算法,即书中作者所说的“扫描算法”,其实就是我们常用的动态规划。
定义b[j]为数组中前 j 个元素数组中的最大连续子序列和。
状态方程为: b[j] = max(b[j-1] + x[j] , x[j])
int maxsubSumO1(int const array[], int n) {
int j;
int thisSum = 0, maxSum = 0;
for (j = 0; j < n; j++) {
thisSum = max(thisSum + array[j], array[j]);
maxSum = max(maxSum, thisSum);
}
return maxSum;
}
欧几里德算法
计算最大公因数的欧几里德算法 Gcd(m, n)
代码实现:
int Gcd(int m, int n) {
int rem;
while (n > 0) {
rem = m % n;
m = n;
n = rem;
}
return m;
}
通过不断的计算余数直到余数是0 为止,最后的非零元就是最大公因数。