[C算法]分治法之归并排序/最大子段和

分治法

子程序直接调用自己或通过一系列条用语句间接调用自己。分治和递归经常同时应用于算法设计之中,并由此产生许多高校的算法。分解--》求解--》合并

时间复杂度:O(nlogn)

归并排序C版(偏伪代码)

#include "MergeSort.h"
#include<stdio.h>
void MergeSort(int A[],int p,int r)
{
int q;
if(p<r){
    q=(p+r)/2;
    MergeSort(A,p,q);
    MergeSort(A,q+1,r);
    Merge(A,p,q,r)
}


}
void Merge(int A[],int p,int q,int r)
{
    int n1=q-p+1;//一个数组的长度p到q
    int n2=r-q;//一个数组的长度从q+1到r
    int i,j,k;
    int L[50],R[50];
    for(i=0; i<n1; i++)
    {
        L[i]=A[p+i];//把A数组左边放入新的数组
    }
    for(j=0; j<n2; j++)
    {
        R[j]=A[q+1+j];////把A数组右边放入新的数组
    }
    L[n1]=INT_MAX;
    R[n2]=INT_MAX;
    j=0;
    i=0;
    for(k=p; k<r+1; k++)
    {
        if(L[i]<R[j])// 当前元素小于左边的当前元素则取右半边的元素
        {
            A[k]=L[i];
            i++;
        }
        else
        {
            A[k]=R[j];
            j++;
        }
    }

}

归并排序C版本2(偏伪)

#include "Merge.h"
#include<stdlib.h>
#define MAX 65536

void merges(int arr[],int p,int q,int r)
{
    int *left,*right;
    int n1,n2,i,j,k;
    n1=q-p+1;
    n2=r-q;
    if((left=(int*)malloc((n1+1)*sizeof(int)))==NULL)
    {
        perror("malloc error");
        exit(1);
    }
    if((right=(int*)malloc((n2+1)*sizeof(int)))==NULL)
    {
        perror("malloc error");
        exit(1);
    }
//复制数组
    for(i=0; i<n1; i++)
    {
        left[i]=arr[p+1];
    }
    left[i]=MAX;
    for(i=0; i<n2; i++)
    {
        right[i]=arr[q+i+1];
    }
    right[i]=MAX;
//归并开始
    i=0;
    j=0;
    for(k=p; k<=r; k++) //从头到尾遍历
    {
        if(left[i]<right[j])
        {
            arr[k]=right[j];
            j++;
        }
        else
        {
            arr[k]=left[i];
            i++;
        }
    }
    void mergesort(int arr[],int begin,int end)
    {
        int mid;
        if(begin<end)
        {
            mid=(begin+end)/2;
            mergesort(arr,begin,mid);
            mergesort(arr,mid+1,end);
            merges(arr,begin,end);
        }
    }



}

最大子段和

注意是子段肯定是连续的不能跳跃,(-2,11,-4,13,-5,-2)时,子段{11,-4,13}为最大子段,和为20。

第一种情况: 计算 left 到 center 的最大和,记作 leftSum

第二种情况: 计算从 center+1 到 right的最大和,记作 rightSum

第三种情况: 跨边界的和。以center为中心分别向两边计算和。

       a.从 center出发,每次向左边扩张一步,并且记录当前的值S1,如果当前的和比上次的和大,就更新S1,一 直向左扩张到 位置  Left。 

      b.从 center+1出发,每次扩张一步,计算当前的和 为S2,如果当前的值比上次的和 大,那么,就更新S2的 值,一直向右扩张到位置Right。

      c.计算过center的连续值的和,S1+S2的值 Sum。 这个就是跨边界的和。

 上面三种情况考虑计算完成后,最后一步就是,比较三个值中的最大值,取最大值就可以了。

---参考https://www.cnblogs.com/lixing-nlp/p/7629403.html

#include<stdlib.h>
/*求解最大子段*/

int MaxSubSum(int *Array,int left,int right)
{
    int sum=0;
    int i;
    if(left==right)//特殊情况
    {
        if(Array[left]>0)
        {
            sum=Array[left];
        }
        else
        {
            sum=0;
        }
    }
    else
    {
        int center = (left+right)/2;
        int leftsum=MaxSubSum(Array,left,center);
        int rightsum=MaxSubSum(Array,center+1,right);
        int s1=0;
        int lefts=0;
        for(i=center; i>=left; i--)//左边的数向左蔓延相加求和
        {
            lefts=lefts+Array[i];
            if(lefts>s1)
            {
                s1=lefts;
            }
        }
        int s2=0;
        int rights=0;
        for(i=center; i<=rights; i++)//右边的数向右蔓延相加求和
        {
            rights=rights+Array[i];
            if(rights>s2)
            {
                s2=rights;
            }
        }
        sum=s1+s2;//过center的值

        if(sum<lefts)
        {
            sum=leftsum;
        }
        if(sum<rightsum)
        {
            sum=rightsum;
        }
    }
    return sum;

}




猜你喜欢

转载自blog.csdn.net/qq_38277033/article/details/79901853