[算法,排序]归并排序算法,实现,比较与测试

[算法,排序]归并排序算法,实现,比较与测试

(Merge Sorting: Implement, Compare and Testing with C)

                                                                                                             By EmilMatthew

                                                                                                                        2005/9/07

归并排序是高级排序算法的一种,所以掌握它是我们继续前进的基础.

要说到归并排序,得谈两件事:

第一件:合并两张已排好序的表(数组),相信这对你来说是一件相当轻松的事情,我们把这个过程叫做merge:

void merge(Type* arr,long left,long mid,long right)

{     //*******attention right=len-1;so,if you want to stand for the len********

       //you have to use right+1;

       long p1,p2;//pointer for pre half arr and post half arr.

       long k;//the pointer for them copy arr

       long i;//iterator num

       Type* arrCopy;

      

       assertF(arr!=NULL,"in merge,pass in arrList is null/n");

       assertF(left<=right,"in merge,left>right/n");

 

       assertF(left<=mid&&mid<=right,"in merge left>mid || mid>right/n");

      

       p1=left;

       p2=mid+1;

      

       k=0;

       arrCopy=(Type*)malloc(sizeof(Type)*(right+1));

      

       while(p1<=mid&&p2<=right)

       {

              if(arr[p1]<=arr[p2])

                     {

                            arrCopy[k]=arr[p1];

                            p1++;

                            k++;

                     }

              else

                     {

                            arrCopy[k]=arr[p2];

                            p2++;

                            k++;

                     }

       }

 

assertF((p1==mid+1&&p2!=right+1)||(p1!=mid+1&&p2==right+1),"in merge ,out of while abnormal/n");

      

       if(p1==mid+1)//attenction.

       {     for(i=p2;i<=right;i++)

                     {

                            arrCopy[k]=arr[i];

                            k++;

                     }

       }

       else if(p2==right+1)

       {

              for(i=p1;i<=mid;i++)

              {

                     arrCopy[k]=arr[i];

                     k++;

              }

       }

      

       /*copy arrCopy data to arr original*/

       listArrCopy2(arrCopy,arr,0,left,right-left+1);

       free(arrCopy);

}

 

不难吧. J

 

2接下来,就要说实现关键了,那又是一个老生常谈的话题---分治思想(Conquer and Divide),主要考虑就是自底向上的进行merge,第一次为每组两个元素的merge,第二次每组4个元素的merge…直至对最后全元素进行一次merge.(全体元素此时已被分成有序的两部分)

a)先说一下递归的版本1:

void mergeSort(Type* inArr,long left,long right)

{     //attention,that here the right=len-1;

       long mid;

       assertF(inArr!=NULL,"in mergerSort,inArr=null/n");

      

       if(left<right)

       {

              mid=(left+right)/2;

              mergeSort(inArr,left,mid);

              mergeSort(inArr,mid+1,right);

              merge(inArr,left,mid,right);

       }

       else return;

}

这个递归的版本和quickSort长得很像(大家的思想一样吗---分治),递归深度优先到底,然后自底向上开始做merge.我们把它叫做Recursive Version1

有个具体的图图,:

http://student.zjzk.cn/course_ware/data_structure/web/paixu/paixu8.5.1.3.htm

 

这个递归版本在测试中表现的相当低效(见最后的测试列表),原因在于我在每个merge中大量使用了拷贝数组元素的动作.

于是,对这个拷贝动作做了一些改进,改成在外层的MergeSort中做拷贝,结果速运行效率果然提高不少:

这个改进版的mergemergeSort(递归版)

b)

void mergeSort2(Type* inArr,long left,long right)

{    

       //attention,that here the right=len-1;

       long mid,len;

       Type* bArr;

       assertF(inArr!=NULL,"in mergerSort,inArr=null/n");

      

       len=right-left+1;

       bArr=(Type*)malloc(sizeof(Type)*len);

      

       if(left<right)

       {

              mid=(left+right)/2;

              mergeSort2(inArr,left,mid);

              mergeSort2(inArr,mid+1,right);

              merge2(inArr,bArr,left,mid,right);

              listArrCopy2(bArr,inArr,0,left,len);

       }

      

       free(bArr);

       return;

}

 

/*Recursive Version of mergeSort2*/

void merge2(Type* arr,Type* arrCopy,long left,long mid,long right)

{     //*******attention right=len-1;so,if you want to stand for the len********

       //you have to use right+1;

       long p1,p2;//pointer for pre half arr and post half arr.

       long k;//the pointer for them copy arr

       long i;//iterator num

      

       assertF(arr!=NULL,"in merge2,pass in arrList is null/n");

       assertF(arrCopy!=NULL,"in merge2,pass in arrList is null/n");

       assertF(left<=right,"in merge2,left>right/n");

       assertF(left<=mid&&mid<=right,"in merge2, left>mid || mid>right/n");

      

       p1=left;

       p2=mid+1;

      

       k=0;

      

       while(p1<=mid&&p2<=right)

       {

              if(arr[p1]<=arr[p2])

                     {

                            arrCopy[k]=arr[p1];

                            p1++;

                            k++;

                     }

              else

                     {

                            arrCopy[k]=arr[p2];

                            p2++;

                            k++;

                     }

       }

assertF((p1==mid+1&&p2!=right+1)||(p1!=mid+1&&p2==right+1),"in merge ,out of while abnormal/n");

 

       if(p1==mid+1)//attenction.

       {     for(i=p2;i<=right;i++)

                     {

                            arrCopy[k]=arr[i];

                            k++;

                     }

       }

       else if(p2==right+1)

       {

              for(i=p1;i<=mid;i++)

              {

                     arrCopy[k]=arr[i];

                     k++;

              }

       }

 

}

 

c)最后给出的是一个非递归的版本,其实,它的想法也是很好理解的,但我实现它却花了不少时间.我看了书,以为理解了,兴奋的合上书,以自己的理解去编制这个非递归版本,可是发现它就是不对,原因在于我对于最后的多余的,不是正好是2^(n-1)的个数的元素处理不到位.

再回过头去看一下书,才发现这里还是很巧妙的,不细加分析一下是很容易出错的.

因为在第j次迭代中,最后需要处理的多余的还没有排好序的剩余元素只有2^j-12^j,所以,在有多余元素的时候,处理的技巧便决定了整个程序正确的关键,而主迭代过程还是好理解的:

/*Unrecursive version of mergeSort*/

void mergeSort3(Type* inArr,long left,long right)

{

       long t,i;

      

       assertF(inArr!=NULL,"in mergeSort3,inArr=null/n");

       assertF(left<=right,"in mergeSort3,left>right/n");

       /*init the data*/

       t=1;//currrent width.

      

       while(t<right+1)

       {

              t*=2;

              i=0;//current point

              while(i+t-1<=right)

              {

                     merge(inArr,i,i+t/2-1,i+t-1);

                     i+=t;

              }

              if(i+t/2-1<right)

              {

              //     assertF(i+t-1>right,"in mergeSort3 adjusting,i+t>=right/n");

                     merge(inArr,i,i+t/2-1,right);  

              }

       }

}

 

 

接下来是对这个mergeSort进行的针对生成的随机数数组进行的排序测试,虽然它们的运算效率书上都有了理论值(O(n*log(n))),但只有你亲自去尝试,才会知道这里面还是有差别的.

这里的三个版本的mergeSort,只有第二个非改进过的递归版的mergeSort表现出了高性能(略低于理论运行效率相同的quickSort)别的版本的mergeSort的运行效率就不敢恭维了,其中的原因,就在一这个mergeSort中包含了大量的数组元素的拷贝(我用的当然已经是十分高效的指针了,但数组元素一多之后还是招架不住啊~~~)

测试结果如下:

MergeSort系列测试(时间单位:s)

 

MergeSort(Recursive Version1)

 

10,000个随机数

100,000个随机数

1,000,000个随机数

10,000,000个随机数

test1

0.094

16.047

未做

未做

test2

0.094

18.265

未做

未做

test3

0.079

19.266

未做

未做

 

MergeSort(Recursive Version2)

 

10,000个随机数

100,000个随机数

1,000,000个随机数

10,000,000个随机数

test1

0.031

0.234

2.547

27.437

test2

0.016

0.235

2.594

26.422

test3

0.016

0.234

2.532

26.125

 

MergeSort(UnRecursive Version)

 

10,000个随机数

100,000个随机数

1,000,000个随机数

10,000,000个随机数

test1

0.094

18.232

未做

未做

test2

0.078

17.375

未做

未做

test3

0.094

15.234

未做

未做

 

 

为了便于做个比较,给出上次做的quickSort(

http://blog.csdn.net/EmilMatthew/archive/2005/09/04/471018.aspx

)的测试结果表,可以看出,第二个改进的递归版本的mergeSort的运行效率还是不错的.(:quickSort的测试中,没做一万个元素的数组的测试)

 

Quick Sort

 

100,000个随机数

1,000,000个随机数

10,000,000个随机数

Test1

0.047000s

0.562000s

10.344000s

Test2

0.047000s

0.578000s

10.860000s

Test3

0.047000s

0.562000s

10.282000s

 

Random Quick Sort

 

100,000个随机数

1,000,000个随机数

10,000,000个随机数

Test1

0.218000s

2.578000s

30.812000s

Test2

0.219000s

2.562000s

31.156000s

Test3

0.218000s

2.594000s

30.956000s

 

 

我所写的程序的源码下载:

http://free3.e-168.cn/as2forward/downloads/mergeSort.rar

//如果上面这个链接无法响应下载(有可能是被网站给屏蔽掉了),则可使用下载工具(如迅雷等)下载

我所写的程序的源码下载:

http://free3.e-168.cn/as2forward/downloads/mergeSort.rar

//如果上面这个链接无法响应下载(有可能是被网站给屏蔽掉了),则可使用下载工具(如迅雷等)下载

 

 

,有些网站也介绍归并排序的内容,有兴趣的请访问:

http://www.d263.com/netxiao/computer/shuiping/200504/netxiao_20050420185107.htm

http://ds.ytu.edu.cn/document/c_jiaoan/c28.htm

 

 

                                      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值