简析归并排序

归并排序,其实质也就是分而治之(Divide and Conquer)+合而为一


分而治之:

1,对当前的这个序列,将其分成两个子序列(合理划分)

2,分别查看子序列的长度,如果小于等于1,那么退出

3,继续执行条件1

如上图:说白了分而治之在这里就是跟递归差不多,将复杂的问题归约的简单化



合而为一:

将当前有序的两个子序列合并成一个有序的序列的过程

如上分析,我们可以把每一个数都当做有序的一个子序列,然后进行合并

接下来考虑下如何将将二个有序数列(含有多个元素)合并。这个非常简单,只要比较二个数列的第一个数,谁小就先取谁,取了后就后移对应的这个数列中的标记。再进行比较两个子序列,如果有标记移到最后,那直接将另一个数列的数据依次取出即可


#include <stdio.h>                                                                                                                              


void merge(int * a, int ii, int * b, int jj, int * c) //待排的两个数组啊,b,分别含有的数的个数是ii和jj,c是待放数组
{
        int i,j,k;
        int ll;
        i = j = k = 0;
        while(i < ii && j < jj)     //循环退出的条件是,a数组排完啦或者b数组排完啦
        {
                if(a[i] <= b[j])
                {
                        c[k] = a[i]; k++; i++;  //不断的往数组c里存储值
                }
                if(a[i] >= b[j])
                {
                        c[k] = b[j]; k++; j++;  //不断的往数组c里存储值
                }

        }

        if(i >= ii)     //如果a数组排完啦,就将b数组之后的元素整体放在c数组后面
        {
                while(j < jj)
                {
                        c[k] = b[j]; k++; j++;
                }
        }
        if(j >= jj)
        {
                while(i < ii)
                {
                        c[k] = a[i]; k++; i++;
                }
        }
}

 int main()
{
        int i;
        int a[4] = {2, 3, 5, 9};        //待排数组a,里面有4个元素
        int b[5] = {1, 4, 6, 7, 8};   //待排数组b,里面有5个元素
        int c[9] = {0};
        
        merge(a, 4, b , 5, c);
        
        for(i =0; i<9;i++)
        {
                printf("%d  ", c[i]);
        }
        printf("\n");
        return 0;
}       

运算结果:

可以看出合并两个有序数列的效率挺高的,达到了O(N)



这样,我们就合并完两个有序数列

所以说:分而治之,就是将其划分为一个个小的有序序列,然后再不断的合并这些小的有序序列,直到所有的有序序列都合并完成



当时在写程序的时候,我碰到了以下的问题:

对于一个数组,我如何将其拆开为两个数组比较好呢???


1,我们要将其拆开为两个数组啊,那么肯定需要两个数组名

int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
int *b;
b = a + n/2;
mergesort(a, b, n/2);       //两个数组:a,b 其中b是指向a[4]的地址

我的本意是想用a,b两个数组将源数组a分成两个小的数组,但是这个貌似不合适递归,我们每一次还得重新定义b,将b指向一个合理的位置,哎,太麻烦了!!!

2,

int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
mergesort(a, n/2 , a+n/2, n);    

恩恩,这样貌似好多了,不用重新定义了,但是当我们真正写代码的时候就会发现,这样写,递归的时候好像找不到第一个子序列的下界,所以还是不好的,不行,还是得改进

3,

int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
mergesort(a,  0,n/2 , a+n/2,n/2, n);
恩,这回应该好多了,第一个子序列(0-n/2),第二个子序列是(n/2 - n),可以满足我们的要求了,但是,参数有点太多了吧!!!

4,

int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
mergesort(a,  0,n/2 ,  n);
哈哈,这回应该好啦,我们可以将原始的数组分为两个小的子序列

a(0     -     n/2)

a(n/2     -     n)

嗯嗯,这样就特别好啦,不仅看起来简介,主要是实现分而治之起来相对方便(递归)


#include <stdio.h>                                                                                                                              


void mers(int *a, int begin,int middle,int  end)
{
        int num = end - begin;
        int num1 = middle - begin;
        int num2 = end - middle;
        int aa[num1];
        int bb[num2];
        int i, j=0, k=0;
        int ii=0, jj=0, kk=0;
        int c[num];
        for(i = begin; i<end; i++)
        {
                if(i<middle){
                        aa[j] = a[i];
                        j++;
                }
                if(i>=middle){
                        bb[k] = a[i];   
                        k++;
                }
        }
        while(jj < j && kk < k)
        {
                if(aa[jj] <= bb[kk])
                {
                        c[ii] = aa[jj]; ii++; jj++;     
                }
                if(aa[jj] > bb[kk])
                {
                        c[ii] = bb[kk]; ii++; kk++;
                }
        }
        if(jj >= j)
        {
                while( kk < k)
                {
                        c[ii] = bb[kk]; ii++; kk++;     
                }       
        }
        if(kk >= k)
        {
                while( jj < j)
                {
                        c[ii] = aa[jj]; ii++; jj++;     
                }
        }
        
        i = 0;
        int BBegin = begin;
        for(;i < num; i++,BBegin++)
                a[BBegin] = c[i];
        printf("%d \n", num);
        for(i=begin;i<end;i++)
                printf("%d  ", a[i]);
        printf("\n");
        getchar();
}

void mergesort(int *a, int begin, int  middle, int end)
{
        if(begin <= middle && middle <= end){   
                if(middle - begin == 1)
                {
                        printf("begin %d  , middle:%d, end:%d\n", begin, middle, end);
                        mers(a, begin , middle, end);
                }
        
                if(begin == middle || middle == end)
                        return ;

                mergesort(a, begin, (middle-begin)/2+begin , middle);
                mergesort(a, middle, (end - middle)/2+middle , end);

                if((middle - begin == 2) || (middle - begin == 4))
                {
                        printf("begin %d  , middle:%d, end:%d\n", begin, middle, end);
                        mers(a, begin , middle, end);
                }

        }
}

int main()
{
        int i;
        int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
        int n = 8;
        mergesort(a, 0 , n/2, n);       //两个数组:a[0 - (n/2-1)], a[n/2 - n]
        for(i = 0; i< 8; i++)
                printf("%d  ", a[i]);
        printf("\n");
}     

嗯嗯,这样我们的归并就完成了

归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值