0.分治永远大于顺序?关于最大子序列和问题的思考

p17. 2.4.3 最大子序列和的问题的解

 题目:给定整数A1,A2,......,AN,k=i~jAk的最大值(如果所有整数都为负数,则最大子序列和为0)

书中给出了四种不同的算法,时间复杂度依次降低,下面我简单描述一下这四种算法

第一种:穷举法

求出所有子序列和,比较得出最大的

最简单想到的代码,效率十分低下,原因是没有利用数列的连贯性,对数列元素反复检索造成浪费

三层嵌套循环,时间复杂度

T(N)=O(N3)

 

int 
Maxsubsum(const int A[],int N)
{
    int ThisSum,Maxsum,i,j,k;
    
    Maxsum=0;                     
    for(i=0;i<N;i++)              //确定各个子序列的首项
        for(j=i;j<N;j++)            //确定每个序列的元素个数
        {
            ThisSum=0;
            for(k=i;k<=j;k++)              /*开始按照确定的元素个数选出数列并计算子列和,较大者覆盖Maxsum,较小者在ThisSum中被丢弃*/
                ThisSum += A[k];

            if(ThisSum>Maxsum)
                Maxsum=ThisSum;
        }

    return Maxsum;
}

 

第二种:改良后的穷举法

 

顾名思义,这种方法对方法一进行了改良.

当第选定了子序列的首项后,无需对子序列长度进行再次分组,直接顺序列出所有该首项下的子数列即可,减去了一次循环。

实际上换汤不换药

时间复杂度依旧为幂函数级

T(N)=O(N2)

int 
Maxsubsum(const int A[],int N)
{
    int ThisSum,Maxsum,i,j,k;
    
    Maxsum=0;                     
    for(i=0;i<N;i++)//确定各个子序列的首项
    {
        ThisSum=0;
        for(k=i;k<=j;k++)/*开始按照确定的元素个数选出数列并计算子列和,较大者覆盖Maxsum,较小者在ThisSum中被丢弃*/
            {
                ThisSum += A[k];

                if(ThisSum>Maxsum)
                Maxsum=ThisSum;
            }
    }

    return Maxsum;
}

 

第三种:分治法

先上代码。

 1 #include "stdio.h"
 2 int
 3 MaxSubSum(const int A[],int left,int right)//left,right分别为数列数组第一个元素和最后一个元素的下标
 4 {
 5     int Maxleftsum,Maxrightsum;
 6     int Maxleftbordersum,Maxrightbordersum;
 7     int leftbordersum,rightbordersum;
 8     int Center,i;
 9 
10     if(left==right)    /*base case*/
11         if(A[left]>0)
12             return A[left];
13         else
14             return 0;
15 
16 
17     Center=(left+right)/2;
18     Maxleftsum=MaxSubSum(A,left,Center);
19     Maxrightsum=MaxSubSum(A,Center+1,right);
20 
21     Maxleftbordersum=0;leftbordersum=0;
22     for(i=Center;i>=left;i--)
23     {
24         leftbordersum +=A[i]
25         if(leftbordersum>Maxleftbordersum)
26             Maxleftbordersum=leftbordersum;
27     }
28 
29     Maxrightbordersum=0;rightbordersum=0;
30     for(i=Center+1;i<=right;i++)
31     {
32         rightbordersum +=A[i]
33         if(rightbordersum>Maxrightbordersum)
34             Maxrightbordersum=rightbordersum;
35     }
36 
37     return Max(Maxleftbordersum,Maxrightbordersum,Maxrightbordersum+Maxleftbordersum)
38 
39 }

 看起来可能有些复杂哈,我第一次看也是半天看不懂,没有这方面的基础。

分治法留到未来再写好了(因为我现在也没有掌握😂😂😂),只需要知道这种算法虽然写起来很复杂,但很高效

时间复杂度为

T(N)=O(N*LogN)

 

第四种:

//本算法将时间复杂度缩减至T(N)=O(N),未用到分治算法T(N)=O(NlogN),但是代码精简,也是一种高速的解题方法
//传统理解使用穷举列出所有序列T(N)=O(N^3),十分臃肿

#include <stdio.h>
int max_son(int a[],int N){
    int tempmax=0;
    int realmax=0;

    for (int i = 0; i < N; ++i)
    {
        tempmax+=a[i];

        if(tempmax>realmax)
            realmax=tempmax;
        if(tempmax<0)
            tempmax=0;

    }
    return realmax;
}
int main(int argc, char const *argv[])
{
    int N;
    printf("how many numbers in your array?\n");
    scanf("%d",&N);
    printf("please enter them in\n");
    int a[N];
    for (int i = 0; i < N; ++i)
    {
        scanf("%d",&a[i]);
    }

    printf("max son array's accumlization is %d\n",max_son(a,N) );
    return 0;
}

请读者自行理解其中奥妙,非常好理解,易读性也很高,时间复杂度也最低。

是这个问题最优解之一。

它有个名字,

“累积遍历算法”。

 

 

还有一个方法叫做动态规划,现在还未学到,未来再来填坑。

 

posted @ 2018-10-21 23:00  小菜鸡想成为算法大神  阅读(196)  评论(0编辑  收藏  举报