1. 程式人生 > >數據結構(一)-----4種方法求最大子列和

數據結構(一)-----4種方法求最大子列和

include iss 需要 中間 () log 完整 font sso

數據結構(一)-----4種方法求最大子列和

1、暴力算法

/*
作者:mys
功能:求最大子列和
日期:2018/7/23
*/
#include<stdio.h>
#include<stdlib.h>
#define N 1000
int maxSubseSum(int a[], int n);
void main()
{
    int a[N] = { 0 },i;
    for (i = 0; i < N; i++)
        a[i] = rand() % 10000;
    printf("maxSubseSum=%d\n", maxSubseSum(a, N));
    system("pause");
}

int maxSubseSum(int a[],int n)
{
    int thisSum, maxSum = 0;
    int i, j, k;
    for (i = 0; i < n; i++)//i是子列左端位置
    {
        for (j = i; j < n; j++)//j是子列右端位置
        {
            thisSum = 0;//thisSum:a[i]到a[j]的子列和
            for (k = i; k <= j; k++)
            {
                thisSum += a[k];
                if (thisSum>maxSum)//更新結果
                    maxSum = thisSum;
            }
        }
    }
    return maxSum;
}

因為有3重for循環,因此時間復雜度為:T(N)=O(N^3),時間復雜度太大,不理想,下面是稍微優化後的。

2、稍微優化

#include<stdio.h>
#include<stdlib.h>
#define N 1000
int maxSubseSum(int a[], int n);
void main()
{
    int a[N] = { 0 }, i;
    for (i = 0; i < N; i++)
        a[i] = rand() % 10000;
    printf("maxSubseSum=%d\n", maxSubseSum(a, N));
    system("pause");
}

int maxSubseSum(int a[], int n)
{
    int thisSum, maxSum = 0;
    int i, j;
    for (i = 0; i < n; i++)//i是子列左端位置
    {
        thisSum = 0;//thisSum:a[i]到a[j]的子列和
        for (j = i; j < n; j++)//j是子列右端位置
        {
                thisSum += a[j];
                if (thisSum>maxSum)//更新結果
                    maxSum = thisSum;

        }
    }
    return maxSum;
}

T(N)=O(N^2)
時間復雜度稍微好一點,但還是比較大,程序員一般看到時間復雜度是N^2,就會想辦法將其復雜度變為NlogN,因此出現了下面的分而治之法。

3、分而治之

/*
作者:mys
功能:求最大子列和(分而治之)
*/
#include<stdio.h>
#include<stdlib.h>
#define N 1000

//求三個數的最大值
int Max(int a, int b, int c)
{
    int max;
    max = (a > b) ? a : b;
    max = (max > c) ? max : c;
    return max;
}

//遍歷整個子列求最大值
int maxCross(int a[], int left, int mid, int right)
{
    int leftSum=0,rightSum=0,sum=0;
    int i;
    //遍歷從中間到左邊
    for (i = mid; i >=left; i--)
    {
        sum += a[i];
        if (sum > leftSum)
            leftSum = sum;
    }
    //遍歷從中間到右邊
    sum = 0;
    for (i = mid + 1; i <= right; i++)
    {
        sum += a[i];
        if (sum > rightSum)
            rightSum = sum;
    }
    return leftSum + rightSum;
}

//分而治之
int divideAndRule(int a[], int left,int right)
{
    int mid=0;
    int maxLeft=0, maxRight=0, maxMiddle=0;
    //如果只有一個數
    if (left == right)
    {
        if (a[left] > 0)
            return a[left];
        else 
            return 0;
    }
    //求中間值
    mid = (left + right) / 2;
    //對左邊的子列用分而治之法
    maxLeft = divideAndRule(a, left, mid);
    //對右邊的子列用分而治之法
    maxRight = divideAndRule(a, mid + 1, right);
    //遍歷整個子列
    maxMiddle = maxCross(a,left,mid,right);
    return Max(maxLeft, maxRight, maxMiddle);
}

void main()
{
    int a[N] = { 0 }, i;
    for (i = 0; i < N; i++)
        a[i] = rand() % 10000;
    printf("divideAndRule=%d\n", divideAndRule(a, 0, N - 1));
    system("pause");
}

此程序的時間復雜度計算如下:
技術分享圖片
T(N)為程序的整個遞歸的時間復雜度,因此前半的時間復雜度為T(N/2);不斷替換,直到T(1);其中N/2^k=1是因為N不斷除2,約為k次,因此k=logN 註:復雜度分析小竅門
這個時間復雜度為NlogN,還算比較優化,但還不是最優的方法,下面這個在線處理方法算是最優化的,時間復雜度T(N)=O(N),就算遍歷完整個子列也需要O(N),因此這個算法是最優化的了。

4、在線處理

/*
作者:mys
功能:求最大子列和(在線處理)
*/
#include<stdio.h>
#include<stdlib.h>
#define N 1000

void main()
{
    int a[N] = { 0 }, i;
    for (i = 0; i < N; i++)
        a[i] = rand() % 10000;
    printf("onlineProcess=%d\n", onlineProcess(a,N));
    system("pause");
}

int onlineProcess(int a[], int n)
{
    int sum = 0, maxSum = 0;
    int i;
    for (i = 0; i < n; i++)
    {
        sum += a[i];//向右累加
        if (sum>maxSum)
            maxSum = sum;//更新結果
        else if (sum < 0)//如果當前子列和為負,則不可能使後面部分的值增大,因此舍去
            maxSum = 0;
    }
    return maxSum;
}

下圖是這四個算法的運行時間比較

技術分享圖片
NA表示not avaliable,從上可看出分而治之O(NlogN)在線處理O(N) 算法還是比較好。

----------------------------------------------------------------------------------2018.7.23以上是我今天整理的數據結構的一點小小的筆記,以後還會繼續更新^-^

數據結構(一)-----4種方法求最大子列和