十大排序算法 之 归并算法

68 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

今天学习归并算法。归并算法的思想是分治法.

思想: 通过自上问下进行分组,把无序的列表一直分到只有一个对象,也就是排好序的最小单位;再自下而上进行排序,直到返回第一层。 流程:

  1. 申请与待排序列表 同样大小的空间。
  2. 使用递归或者循环方式,分成两组,直至分组到1(全部有序);
  3. 比较 大小,并使用额外的空间作为缓存,合并有序列表,再次复制到原列表的空间。
  4. 循环处理,完成全部分组的合并。

归并算法的时间复杂度是O(nlogn),在比较过程中,可以确定对象的相对位置不换位,所以算法是稳定的。 对于大数据排序来说,比较合适使用。

代码实例:

#include<stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <string.h>
#include <errno.h>

#define SWAP(a1, a2, type) do {type temp = a1; a1 = a2; a2 = temp;} while(0)



int ArrCnt = 0;
int NumArr[200] = {0};

void PrintNums()
{
    for(int i = 0;i < ArrCnt;i++){
        fprintf(stdout," %d-",NumArr[i]);
    }
}


//升序排序
int OpsMgArr(int * Arr,int pos,int cnt,int *Buf)
{
    int LeftId = pos;
    int LeftCnt = cnt/2;

    int RightId = LeftId + LeftCnt;
    int RightCnt = cnt/2+cnt%2;

   // fprintf(stdout,"LeftId [%d]-LeftCnt[%d]\n",LeftId,LeftCnt);
   // fprintf(stdout,"RightId [%d]-RightCnt[%d]\n",RightId,RightCnt);

    if(2 < LeftCnt){
        OpsMgArr(Arr,pos,LeftCnt,Buf);
    }else{
        //最小分组,比较,并进行交换排序 左侧
        if(LeftCnt == 2 && (Arr[LeftId+1] < Arr[LeftId])) SWAP(Arr[LeftId+1],Arr[LeftId],int);  
    }
    if(2 < RightCnt){
        OpsMgArr(Arr,RightId,RightCnt,Buf);
    }else{
        //最小分组,比较,并进行交换排序 右侧
        if(RightCnt == 2 && (Arr[RightId+1] < Arr[RightId])) SWAP(Arr[RightId+1],Arr[RightId],int);
    }

    int AllCnt = LeftCnt + RightCnt;

    for(int a = 0;a < AllCnt;a++){
        if(RightCnt == 0 || (LeftCnt && (Arr[LeftId] <= Arr[RightId]))){
            Buf[a] = Arr[LeftId];
            LeftId++;
            LeftCnt--;
        }else{
            Buf[a] = Arr[RightId];
            RightId++;
            RightCnt--;
        }
    }

    for(int b = 0;b < AllCnt;b++){
         Arr[pos+b] = Buf[b];
        fprintf(stdout,"Arr[%d]=[%d]-",pos+b,Arr[pos+b]);
    }
    fprintf(stdout,"\n\n\n");

    return 0;
}
int MergeSort(int Cnt,int* ArrPtr)
{
    int *ptrTemp = (int*)malloc(Cnt*4);
    memset(ptrTemp,0,Cnt*4);
    OpsMgArr(ArrPtr,0,Cnt,ptrTemp);
    if(ptrTemp ) free(ptrTemp);
    return 0;
}
int main(int argc, char** argv)
{
    if(argc == 1) {
        fprintf(stderr,"%s:need a some arg\n",argv[0]);
        exit(1);
    }
    int CntTemp = ArrCnt = argc -1;

    for(int i = 0;i < ArrCnt;i++){
        NumArr[i] = atoi(argv[i+1]);
    }
   
    fprintf(stdout,"\t\t\t\t[Merge Sort]inputNum:");
    PrintNums();
    fprintf(stdout,"\n");

    MergeSort(ArrCnt,NumArr);

    fprintf(stdout,"\t\t\t\tresult:");
    PrintNums();
    fprintf(stdout,"\n");
    return 0;

}

测试结果:

image.png 有点啰嗦,不过加了一些日志没删除,有兴趣的可以编译出来,自己验证一下,有错误的话,请联系我。

实际开发中,还是尽量不要用递归调用,往往调用栈太深,占据太多内存。可以改成循环调用逻辑。一般的,使用递归的逻辑都可以改成循环逻辑。