码迷,mamicode.com
首页 > 编程语言 > 详细

向量篇_归并排序的实现及讨论

时间:2018-09-07 20:10:10      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:结构   []   关系   存储设备   运行时间   pre   nlog   单元   组织   

一、前言

    归并排序由冯•诺依曼于1945年在EDVAC上首次编程实现,归并排序(mergesort)的构思朴实却亦深刻,作为一个算法既古老又仍不失生命力。在排序算法发展的历史上,归并排序具有特殊的地位,它是第一个可以在最坏情况下依然保持O(nlogn)运行时间的确定性排序算法。

    时至今日,在计算机早期发展过程中曾经出现的一些难题在更大尺度上再次呈现,归并排序因此重新焕发青春。比如,早期计算机的存储能力有限,以至于高速存储器不能容纳所有的数据,或者只能使用磁带机或卡片之类的顺序存储设备,这些既促进了归并排序的诞生,也为该算法提供了施展的舞台。信息化无处不在的今天,我们再次发现,人类所拥有信息之庞大,不仅迫使我们更多地将它们存放和组织于分布式平台之上,而且对海量信息的处理也必须首先考虑,如何在跨节点的环境中高效地协同计算。因此在许多新算法和技术的背后,都可以看到归并排序的影子。

二、算法思想及实现

    首先来了解二路归并算法,二路归并属于迭代式算法。每步迭代中,只需比较两个待归并向量的首元素,将小者取出并追加到输出向量的末尾,该元素在原向量中的后继则成为新的首元素。如此往复,直到某一向量为空。最后,将另一非空的向量整体接至输出向量的末尾。归并排序也可以理解为是通过反复调用所谓二路归并(2-way merge)算法而实现的。所谓二路归并,就是将两个有序序列合并成为一个有序序列。这里的序列既可以是向量,归并排序所需的时间,也主要决定于各趟二路归并所需时间的总和。

    可见,二路归并算法在任何时刻只需载入两个向量的首元素,故除了归并输出的向量外,仅需要常数规模的辅助空间。另外,该算法始终严格地按顺序处理输入和输出向量,故特别适用于使用磁带机等顺序存储器的场合。

    实现如下:

    

 1  //向量的归并排序
 2 template <typename T>
 3 void Vector<T>::mergeSort ( Rank lo, Rank hi ) { //0 <= lo < hi <= size
 4     if ( hi - lo < 2 ) return; //单元素之间必然有序,用作递归基
 5     int mi = ( lo + hi ) >> 1; 
 6     mergeSort ( lo, mi ); //以中点为界分别排序
 7     mergeSort ( mi, hi ); 
 8     merge ( lo, mi, hi ); //二路归并
 9  }
10 
11 //有序向量的二路归并算法
12 template <typename T> 
13 void Vector<T>::merge ( Rank lo, Rank mi, Rank hi ) { //各自有序子向量[lo, mi)和[mi, hi)
14     T* A = _elem + lo; //合并后的向量A[0, hi - lo) = _elem[lo, hi)
15     int lb = mi - lo; 
16     T* B = new T[lb]; //前子向量B[0, lb) = _elem[lo, mi)
17     for ( Rank i = 0; i < lb; B[i] = A[i++] ); //复制前子向量
18     int lc = hi - mi; 
19     T* C = _elem + mi; //后子向量C[0, lc) = _elem[mi, hi)
20     for ( Rank i = 0, j = 0, k = 0; ( j < lb ) || ( k < lc ); ) { //B[j]和C[k]中的小者续至A末尾
21         if ( ( j < lb ) && ( ! ( k < lc ) || ( B[j] <= C[k] ) ) ) A[i++] = B[j++];
22         if ( ( k < lc ) && ( ! ( j < lb ) || ( C[k] < B[j] ) ) ) A[i++] = C[k++];
23 }
24     delete [] B; //释放临时空间B
25 } //归并后得到完整的有序向量[lo, hi)        

三、分析及改进

    那么,基于以上二路归并的线性算法,归并排序算法的时间复杂度又是多少呢?不妨采用递推方程分析法,为此首先将归并排序算法处理长度为n的向量所需的时间记作T(n)。根据算法构思与流程,为对长度为n的向量归并排序,需递归地对长度各为n/2的两个子向量做归并排序,再花费线性时间做一次二路归并。如此,可得以下递推关系:T(n) = 2*T(n/2) + O(n)另外,当子向量长度缩短到1时,递归即可终止并直接返回该向量。

    故有边界条件T(1) = O(1)  联立以上递推式,可以解得:T(n) = O(nlogn)  也就是说,归并排序算法可在O(nlogn)时间内对长度为n的向量完成排序。因二路归并算法的效率稳定在O(n),故更准确地讲,归并排序算法的时间复杂度应为O(nlogn)。再分析,若B已经提前耗尽,则C可直接转移;若C提前耗尽,则B可直接转移。而我们不必考虑C提前耗尽的情况。则改进后得二路归并的精简实现:

1 for ( Rank i = 0, j = 0, k = 0;  j < lb; ) { 
2         if ( ( k < lc ) &&  ( C[k] < B[j] ) ) A[i++] = C[k++];
3         if ( ! ( k < lc ) || ( B[j] <= C[k] )  ) A[i++] = B[j++];
4  }  //交换循环体内的两句次序,删除冗余逻辑。

四、总结

    这一算法框架也可应用于另一类典型的序列结构—列表,而且同样可以达到线性的时间效率。

 

向量篇_归并排序的实现及讨论

标签:结构   []   关系   存储设备   运行时间   pre   nlog   单元   组织   

原文地址:https://www.cnblogs.com/parzulpan/p/9606641.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!