最大字段和各种不同算法实现(参考编程珠玑)
求最大字段和的算法很好的讲解了算法设计技术。根据《编程珠玑》上的描述,简单实现各种不同的算法。如下:
1、最简单的方法:对所有满足0≤i≤j<n的(i,j)整数进行迭代。对每个整数对,都计算x[i..j]的总和:
简单方法
int maxSum_easy(int n, int* a, int& besti, int& bestj) //最简单的方法,复杂度为O(n^3) { int maxSoFar = 0, i = 0, j = 0, k = 0, sum = 0; for (i = 0; i < n; ++i) for ( j = i; j < n; ++j) { sum = 0; for (k = i; k<=j; ++k) sum += *(a+k); if (sum > maxSoFar) { maxSoFar = sum; besti = i; bestj = j; } } return maxSoFar; }
2、改进简单的方法,将复杂度变为O(n2),注意子和求解过程,分别有两种改进方法:
方法一,如下:
改进一
int maxSum_O2na(int n, int *a, int& besti, int& bestj)//改进上一算法,时间复杂度变为O(n^2) { int maxSoFar = 0, i = 0, j = 0, k = 0, sum = 0; for (i = 0; i < n; ++i) { sum = 0; for (j = i; j < n; ++j) { sum += *(a+j); if (sum > maxSoFar) { maxSoFar = sum; besti = i; bestj = j; } } } return maxSoFar; }
方法二,如下:
改进二
int maxSum_O2nb(int n, int *a, int& besti, int& bestj) //增加一累加数组,时间复杂度变为O(n^2) { int maxSoFar = 0, i = 0, j = 0, sum = 0; int * acc = new int[n+1]; //创建一个额外的累加器 *(acc) = 0; for (i = 1; i <= n; ++i) *(acc+i) = * (acc+i-1) + *(a+i-1); for (i = 0; i < n; ++i) for (j = i; j < n; ++j) { sum = *(acc+j+1) - *(acc+i); // sum is sum of a[i..j] if (sum > maxSoFar) { maxSoFar = sum; besti = i; bestj = j; } } delete acc; return maxSoFar; }
3、同时,还可以利用分治法对其进行求解,复杂度为O(nlogn)代码如下:
分治法求解
int maxSum_ConDiv(int *a, int left, int right)//分治法,复杂度为O(nlogn),调用形式为maxSum_ConDiv(a,0,n-1) { int maxSoFar = 0, sum = 0, i = 0, m = (left+right)/2, lmax =0, rmax = 0, lsub = 0, rsub =0; if (left > right) //zero elements return 0; if (left == right) //one element return left > 0 ? left : 0; for (i = m; i >= left; --i) { sum += *(a+i); if (sum > lmax) lmax = sum; } sum = 0; for (i = m+1; i <= right; ++i) { sum += *(a+i); if (sum > rmax) rmax = sum; } //find max value among the 3 values and return it lsub = maxSum_ConDiv(a,left,m); rsub = maxSum_ConDiv(a,m+1,right); maxSoFar = lmax + rmax; if (lsub > maxSoFar) maxSoFar = lsub; if (rsub > maxSoFar) maxSoFar = rsub; return maxSoFar; }
4、另外一种方法,是效率最高的,复杂度为O(n),利用了动态规划的思想,代码如下:
动态规划求解
int maxSum_sm(int* a, int n) //扫描算法,时间复杂度为O(n) { int maxSoFar = 0, maxendinghere = 0, i =0, temp =0; for (; i < n; ++i) { maxendinghere = maxendinghere+a[i]; maxendinghere = maxendinghere > 0 ? maxendinghere : 0; if (maxendinghere > maxSoFar) maxSoFar = maxendinghere; } return maxSoFar; }
测试实验如下所示:
int main() { int a[] = {31, -41, 59, 26, -53, 58, 97, -93, -23, 84}, n, besti = 0, bestj = 0, maxSum = 0; n = sizeof(a)/sizeof(a[0]); cout << "a的最大子和为:" << maxSum_sm(a,n) << endl; //cout << ";是 " << besti << "->" << bestj << "的和" << endl; return 0; }