最大子序列和的四种求解算法及其时间比较

来源:互联网 发布:编程教育 编辑:程序博客网 时间:2024/05/21 07:04

1、T(N)=O(N^3)

__int64 algorithm1(__int64 a[],__int64 n)    //T(N)=O(N^3){    __int64 MaxSum = 0;    __int64 ThisSum;    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字    {        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字        {            ThisSum = 0;            //PS:第一轮循环k从0->0,0->1,0->2...0->n            //PS:第二轮循环k从1->1,1->2,1->3...1->n            //PS:第K轮循环k从k->k,k->k+1,k->k+2...k->n            //PS:这样就遍历完序列中所有子序列的可能性            for(__int64 k = i; k <= j; k++) //从i开始到j的数字相加                ThisSum += a[k];            if(ThisSum > MaxSum)    //大于最大子序列之和则替换                MaxSum = ThisSum;        }    }    return MaxSum;}

2、T(N)=O(N^2)

__int64 algorithm2(__int64 a[],__int64 n)   //T(N)=O(N^2){    __int64 MaxSum = 0;    __int64 ThisSum;    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字    {        ThisSum = 0;        //PS:第一轮j从0->n,每循环一次,判断一次        //PS:第二轮j从1->n,每循环一次,判断一次        //PS:第J轮j从j->n,每循环一次,判断一次        //PS:这样就遍历完序列中所有子序列的可能性        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字        {            ThisSum += a[j];            if(ThisSum > MaxSum)    //大于最大子序列之和则替换                MaxSum = ThisSum;        }    }    return MaxSum;}

3、T(N)=O(NlogN),底数为2

PS:用分治法,先将原序列分成相似的两个子序列,这样可以得出一个结论,
那就是最大子序列和要不在左子序列中,要不在右子序列中,要不在左子序列和右子序列的连接处
对左子序列递归求最大序列和,对右子序列求最大序列和,再将左右子序列连接起来,求连接处的最大序列和
最后比较就能求出整体最大序列和

__int64 MaxSubSum(__int64 a[],__int64 Left,__int64 Right){    __int64 Center; //中间元素    __int64 MaxLeftSum,MaxRightSum; //左右子序列最大序列和    __int64 MaxLeftBorderSum,MaxRightBorderSum; //左右边界最大序列和    __int64 LeftBorderSum,RightBorderSum;   //左右边界序列和    if(Left == Right)   //递归出口        if(a[Left] > 0) //当a[i]>0时,返回该元素            return a[Left];        else            //否则返回0            return 0;    Center  = (Left + Right) / 2;    MaxLeftSum = MaxSubSum(a,Left,Center);  //递归求解左子序列    MaxRightSum = MaxSubSum(a,Center + 1,Right); //递归求解右子序列    MaxLeftBorderSum = 0;    LeftBorderSum = 0;    for(__int64 i = Center; i>= Left; i--)  //求左边界最大序列和    {        LeftBorderSum += a[i];        if(LeftBorderSum > MaxLeftBorderSum)            MaxLeftBorderSum = LeftBorderSum;    }    MaxRightBorderSum = 0;    RightBorderSum = 0;    for(__int64 i = Center + 1; i <= Right; i++) //求右边界最大序列和    {        RightBorderSum += a[i];        if(RightBorderSum > MaxRightBorderSum)            MaxRightBorderSum = RightBorderSum;    }    return max(max(MaxLeftSum,MaxRightSum),MaxLeftBorderSum+MaxRightBorderSum); //返回整体最大序列和}__int64 algorithm3(__int64 a[],__int64 n)   //T(N)=O(NlogN),底数为2{    return MaxSubSum(a,0,n - 1);}

4、T(N)=O(N)

推荐用这种算法

__int64 algorithm4(__int64 a[],__int64 n)     //T(N)=O(N){    __int64 ThisSum = 0;    __int64 MaxSum = 0;    for(int i = 0; i < n; i++)  //i从零开始到最后一个数字    {        ThisSum += a[i];        if(ThisSum < 0) //当a[j]+...+a[i] < 0,则舍弃i之前的所有序列,从i后继续累加            ThisSum = 0;        if(ThisSum > MaxSum) //求出当前序列的最大子序列和            MaxSum = ThisSum;    }    return MaxSum;}

5、数量级及其时间比较表格

Algorithm1234TimeO(N^3)T/N RateO(N^2)T/N RateO(NlogN)T/N RateO(N)T/N RateInput Size(N)100.9030.09030.7710.07710.7340.07340.7070.07071001.0690.010690.7910.007910.7840.007840.7440.0074410003.5380.0035380.9520.0009520.8050.0008050.780.000781000030000.31.5990.00015991.0670.00010670.8590.000085910000030000003073.8440.000738441.2550.000012551.1250.00001125

6、源代码及其测试数据

#include <iostream>#include <fstream>#include <cstdlib>#include <ctime>using namespace std;__int64 algorithm1(__int64 a[],__int64 n)    //T(N)=O(N^3){    __int64 MaxSum = 0;    __int64 ThisSum;    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字    {        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字        {            ThisSum = 0;            //PS:第一轮循环k从0->0,0->1,0->2...0->n            //PS:第二轮循环k从1->1,1->2,1->3...1->n            //PS:第K轮循环k从k->k,k->k+1,k->k+2...k->n            //PS:这样就遍历完序列中所有子序列的可能性            for(__int64 k = i; k <= j; k++) //从i开始到j的数字相加                ThisSum += a[k];            if(ThisSum > MaxSum)    //大于最大子序列之和则替换                MaxSum = ThisSum;        }    }    return MaxSum;}__int64 algorithm2(__int64 a[],__int64 n)   //T(N)=O(N^2){    __int64 MaxSum = 0;    __int64 ThisSum;    for(__int64 i = 0; i < n; i++)  //i从零开始到最后一个数字    {        ThisSum = 0;        //PS:第一轮j从0->n,每循环一次,判断一次        //PS:第二轮j从1->n,每循环一次,判断一次        //PS:第J轮j从j->n,每循环一次,判断一次        //PS:这样就遍历完序列中所有子序列的可能性        for(__int64 j = i; j < n; j++)  //j从i开始到最后一个数字        {            ThisSum += a[j];            if(ThisSum > MaxSum)    //大于最大子序列之和则替换                MaxSum = ThisSum;        }    }    return MaxSum;}//PS:用分治法,先将原序列分成相似的两个子序列,这样可以得出一个结论,//那就是最大子序列和要不在左子序列中,要不在右子序列中,要不在左子序列和右子序列的连接处//对左子序列递归求最大序列和,对右子序列求最大序列和,再将左右子序列连接起来,求连接处的最大序列和//最后比较就能求出整体最大序列和__int64 MaxSubSum(__int64 a[],__int64 Left,__int64 Right){    __int64 Center; //中间元素    __int64 MaxLeftSum,MaxRightSum; //左右子序列最大序列和    __int64 MaxLeftBorderSum,MaxRightBorderSum; //左右边界最大序列和    __int64 LeftBorderSum,RightBorderSum;   //左右边界序列和    if(Left == Right)   //递归出口        if(a[Left] > 0) //当a[i]>0时,返回该元素            return a[Left];        else            //否则返回0            return 0;    Center  = (Left + Right) / 2;    MaxLeftSum = MaxSubSum(a,Left,Center);  //递归求解左子序列    MaxRightSum = MaxSubSum(a,Center + 1,Right); //递归求解右子序列    MaxLeftBorderSum = 0;    LeftBorderSum = 0;    for(__int64 i = Center; i>= Left; i--)  //求左边界最大序列和    {        LeftBorderSum += a[i];        if(LeftBorderSum > MaxLeftBorderSum)            MaxLeftBorderSum = LeftBorderSum;    }    MaxRightBorderSum = 0;    RightBorderSum = 0;    for(__int64 i = Center + 1; i <= Right; i++) //求右边界最大序列和    {        RightBorderSum += a[i];        if(RightBorderSum > MaxRightBorderSum)            MaxRightBorderSum = RightBorderSum;    }    return max(max(MaxLeftSum,MaxRightSum),MaxLeftBorderSum+MaxRightBorderSum); //返回整体最大序列和}__int64 algorithm3(__int64 a[],__int64 n)   //T(N)=O(NlogN),底数为2{    return MaxSubSum(a,0,n - 1);}__int64 algorithm4(__int64 a[],__int64 n)     //T(N)=O(N){    __int64 ThisSum = 0;    __int64 MaxSum = 0;    for(int i = 0; i < n; i++)  //i从零开始到最后一个数字    {        ThisSum += a[i];        if(ThisSum < 0) //当a[j]+...+a[i] < 0,则舍弃i之前的所有序列,从i后继续累加            ThisSum = 0;        if(ThisSum > MaxSum) //求出当前序列的最大子序列和            MaxSum = ThisSum;    }    return MaxSum;}__int64 Random(__int64 m,__int64 n)    //指定范围内随机数{    __int64 pos,dis;    if(m == n)  //m=n则表示范围内只有一个数字    {        return m;    }    else if(m > n)  //m>n则说明取[m,n]区间内数字    {        pos = n;        dis = m - n + 1;        return rand() % dis + pos;    }    else    //m>n则说明取[n,m]区间内数字    {        pos = m;        dis = n - m + 1;        return rand() % dis + pos;    }}void MakeRandomSequences(__int64 m,__int64 n,__int64 num)   //产生随机序列{    ofstream filerandom("G:\\sequences.txt");    srand((int)time(NULL)); //根据时间产生相应种子值    for(int i=0 ; i < num; i++)    {        filerandom << Random(m,n) <<" ";        if(!((i + 1) % 10)) //数据量逢十换行            filerandom << endl;    }    filerandom.close();}int main(){//测试数据//    __int64 a[] = {4,-3,5,-2,-1,2,6,-2};//    __int64 n = 8;//    cout << algorithm1(a,n) << endl;//    cout << algorithm2(a,n) << endl;//    cout << algorithm3(a,n) << endl;//    cout << algorithm4(a,n) << endl;//    MakeRandomSequences(-1000,1000,100000);  //产生随机序列    __int64 a[200000];    __int64 n = 0;    ifstream filesequ("G:\\sequences.txt");    if(!filesequ.is_open())   //打开失败处理    {        cout<<"Error opening file";        return -1;    }    while(!filesequ.eof())    {        filesequ >> a[n++];    }    filesequ.close();//PS:测试只需要把调用算法注释去掉//    cout << algorithm1(a,n) << endl;//    cout << algorithm2(a,n) << endl;//    cout << algorithm3(a,n) << endl;    cout << algorithm4(a,n) << endl;    return 0;}

0 0
原创粉丝点击