开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情
今天学习归并算法。归并算法的思想是分治法.
思想:
通过自上问下进行分组,把无序的列表一直分到只有一个对象,也就是排好序的最小单位;再自下而上进行排序,直到返回第一层。
流程:
- 申请与待排序列表 同样大小的空间。
- 使用递归或者循环方式,分成两组,直至分组到1(全部有序);
- 比较 大小,并使用额外的空间作为缓存,合并有序列表,再次复制到原列表的空间。
- 循环处理,完成全部分组的合并。
归并算法的时间复杂度是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;
}
测试结果:
有点啰嗦,不过加了一些日志没删除,有兴趣的可以编译出来,自己验证一下,有错误的话,请联系我。
实际开发中,还是尽量不要用递归调用,往往调用栈太深,占据太多内存。可以改成循环调用逻辑。一般的,使用递归的逻辑都可以改成循环逻辑。