归并排序算法之错误修正

来源:互联网 发布:淘宝天猫秒杀抢购软件 编辑:程序博客网 时间:2024/06/05 16:18

归并排序:将两个或者两个以上的有序表合并成一个新的有序表,二路归并就是一组数组中前后相邻的两个有序序列归并为一个有序序列

二路归并的核心思想:假设有n个序列,然后两两归并,得到[n/2]个长度为2或者1的子序列;然后再两两归并,……,直至得到一个长度为n的有序序列为止

看下面2路归并算法的实例:

int MSort(MergeType S, MergeType *pT, int nStart, int nEnd){int nMidPos = 0;if ( !S.elem || nStart > nEnd){return -1;}if (nStart == nEnd){pT->elem[nStart] = S.elem[nStart];return 0;}nMidPos = (nStart + nEnd) / 2;MSort(S, pT, nStart, nMidPos);MSort(S, pT, nMidPos + 1, nEnd);Merge(S, pT, nStart, nMidPos, nEnd);return 0;}

上面的步骤是将数据对半分割,直至只有一个数据,然后再进行归并,这是个递归的操作,看一下如何进行归并操作:

/*S:原数据列表,pT:排序好的数据列表,其他数据和函数定义请参考:冒泡算法的改进*/

int Merge(MergeType S, MergeType *pT, int nStart, int nPos, int nEnd){int nSi = nStart, nSj = nPos + 1;int nTi = nStart;for ( ; nSi <= nPos && nSj <= nEnd; ){   //这里出现错误,因为s与pt是同一个变量,改变一个会影响另一个pT->elem[nTi++] = (S.elem[nSi] <= S.elem[nSj])?S.elem[nSi++]:S.elem[nSj++];}//剩余的数据while (nSi <= nPos){pT->elem[nTi++] = S.elem[nSi++];}while (nSj <= nEnd){pT->elem[nTi++] = S.elem[nSj++];}return 0;}
归并的时候,最初是先归并两个数组(只有一个数据),归并为一个数组,当然还有两个不同的数组(数组维数n>=2)归并为一个数组。但是还会有一些情况:

假设一个数组为(0,10,24) ,另一个为(32,45),这时候(0,10,24)会被填充进数组T中,但是另一个数组(32,45)还没有插入数组T,后面两个while循环,只需将剩余插入数组T尾部,这时候不用进行插入排序比较,一定是插在数组的尾部,for循环比较已经知道剩余数据的位置了。

表面上看,上面的算法是正确的,其实在归并的时候就有问题,已在注释中说明地方,请思考如何修正错误?错误不一定就是那里!

主要是MSort的时候,首先将S排序为pT:MSort(S, &Temp, nStart, nMidPos);然后再将S归并为pT,其实这本身就有问题,其实应该将pT归并为……,PT?这里就有个疑问,是否是pT呢,按理说pT这时候还没有排好序,无所谓,其实是有所谓的,在后面的排序过程中,会修改有些数据的位置,就是注释的地方,所以这里一定要设置一个临时变量,修改如下:

int MSort(SortType &S, SortType *pT, int nStart, int nEnd){int nMidPos = 0;SortType Temp;Temp.elem= (int*)malloc(sizeof(int)*S.len);Temp.len= S.len;Temp.size= S.len;memset(Temp.elem, 0, S.len*sizeof(int));if ( !S.elem || nStart > nEnd){return -1;}if (nStart == nEnd){pT->elem[nStart] = S.elem[nStart];return 0;}nMidPos = (nStart + nEnd) / 2;MSort(S, &Temp, nStart, nMidPos);MSort(S, &Temp, nMidPos + 1, nEnd);Merge(Temp, pT, nStart, nMidPos, nEnd);free(Temp.elem);Temp.elem = NULL;return 0;},
这里我将MergeType类型 修改为SortType,便于记忆,其中的形参MergeType S已经修改为SortType &S,使用了引用形参,这里主要是为了减少形参的复制,且不修改原来列表,其实这里类型应该定义为const SortType &S,但是要修改多处,减少麻烦。如下测试实例:

SortType pList;SortType pT;  pList.elem = (int*)malloc(sizeof(int)*LISTLEN);pList.len  = LISTLEN;pList.size = LISTLEN;ScanfList(&pList);#if 1pT.elem= (int*)malloc(sizeof(int)*LISTLEN);pT.len= 10;pT.size= LISTLEN;memset(pT.elem, 0, LISTLEN*sizeof(int));#endif/*归并排序*///MSort(pList, &pT, 0, LISTLEN);MSort(pList, &pT, 0, pList.len-1);PrintList(&pList);PrintList(&pT);free(pList.elem);free(pT.elem);pList.elem = NULL;pT.elem = NULL;
测试结果如下:

--- SortTest ---
Old List        : 0 10 18 24 28 30 30 28 24 18  
Sort List       : 0 10 18 24 28 30 30 28 24 18
Sort List       : 0 10 18 18 24 24 28 28 30 30

第一行为pList原始数据,第二行为排序后pList中的数据,第三行排序后pT中的数据,可以看出应经排序好了

0 0
原创粉丝点击