给定一组数据:31, -41, 59, 26, -53, 58, 97, -93, -23, 84。要求找到这组数据中连和最大的子序列,相信很多人都看过这个算法,能够直接给出线性时间复杂度的解法。但是还是有其他解法的,而且是循序渐进的,从O(n^3), O(n^2), O(n*logn)到O(n)。
在开始算法之前要进行一下约定,如果当前输入的都是负数的时候,综合最大的子序列是空的,总和为0.
第一种方法就是brute-force解法,就是对满足所有的0<=i < j < n的组合(i,j)进行迭代,对于每个整数对都要计算a[i,j]的总和,这个算法有个特点,也就是造成时间复杂度很高的缺点,对于每次计算的结果没有记录,只是简单的将此次计算的结果和上次计算的结果进行比较,求得当前最大值后,就给扔掉了。下面给出算法:
- current_max = 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 > current_max)
- current_max = sum;
- }
- }
很清楚的可以看出,对i和j的操作过程中,只是对当前和与上次的和做简单的比较,之后就扔弃了。想一想,可不可以这样做,对于特定的i,遍历所有可行的j值,记录此次(i,j)的操作,对于(i,j+1)的操作可以在(i,j)所得和的基础上简单的加a[j+1]就可以了。
下面给出算法:
- current_max = 0;
- for(i = 0; i < n; i++) {
- sum = 0;
- for(j = i; j < n; j++) {
- sum += a[j];
- if(sum > current_max)
- current_max = sum;
- }
- }
这个时间复杂度比上次的有所降低。
同时,这个问题还可以用分治算法来解决,可能会有疑问,因为分治算法是用来解决子问题之间没有联系的问题的。而求连续子序列和最大值得问题更多的和动态规划相关,因为用动态规划很容易解决这个问题。但是分治也可以,这种算法很巧妙,巧妙的让人不可思议。给出算法代码:
- #include<stdio.h>
- int max(int a, int b, int c) {
- if(a > b) {
- if(a > c) {
- return a;
- } else {
- return c;
- }
- } else {
- if(b > c) {
- return b;
- } else {
- return c;
- }
- }
- }
- int get_max(int *a, int low, int high) {
- int m = (low + high) / 2;
- int i;
- int sum, lmax, rmax;
-
- if(low > high)
- return 0;
- if(low == high)
- return a[low] > 0 ? a[low] : 0;
-
- sum = lmax = 0;
- for(i = m; i >= low; i--){
- sum += a[i];
- if(sum > lmax) {
- lmax = sum;
- }
- }
-
- sum = rmax = 0;
- for(i = m + 1; i <= high; i++) {
- sum += a[i];
- if(sum > rmax) {
- rmax = sum;
- }
- }
-
- return max(lmax + rmax, get_max(a, low, m), get_max(a, m + 1, high));
- }
-
- void main() {
- int a[] = {31, -41, 59, 26, -53, 58, 97, -93, -23, 84};
- int n = sizeof(a) / sizeof(int);
- int result = get_max(a, 0, n - 1);
- printf("max_sum = %d\n", result);
- }
这个算法的巧妙之处就在于计算lmax和rmax之处,计算lmax是从m开始到low结束,计算rmax是从m+1开始,在high结束。
最后一种方法就是使用动态规划了,相信很多人能给出此种算法。直接给出代码:
- #include<stdio.h>
- int get_max2(int *a, int n) {
- int current_max = a[0];
- int final_max = a[0];
- int i;
- for(i = 1; i < n; i++) {
- if(current_max + a[i] > 0) {
- current_max += a[i];
- } else {
- current_max = 0;
- }
- if(current_max > final_max) {
- final_max = current_max;
- }
- }
- return final_max;
- }
- void main() {
- int a[] = {31, -41, 59, 26, -53, 58, 97, -93, -23, 84};
- int n = sizeof(a) / sizeof(int);
- int rst = get_max2(a, n);
- printf("max_sum = %d\n", rst);
- }