更新时间:2023-12-19 13:04
排序是计算机程序设计中的一种重要操作,它的功能是将任意序列的数据元素或记录重新按关键字顺序排列成有序的序列。 有序序列为记录的查找、插入和删除提供了方便,可以有效提高搜索效率。因此,研究各类排序方法是计算机研究中的重要课题之一。根据待排序记录数量及其在排序过程中涉及的存储器,可将排序方法分为两大类: 一类是内部排序, 指的是待排序记录存放在计算机存储器中进行的排序过程;另一类是外部排序, 指的是待排序记录的数量很大,以至于内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。
外部排序指的是大文件的排序,当待排序的文件很大时,无法将整个文件的所有记录同时调入内存进行排序,只能将文件存放在外存,这种排称为外部排序。外部排序的过程主要是依据数据的内外存交换和“内部归并”两者结合起来实现的。
一般提到排序都是指内排序,比如快速排序,堆排序,归并排序等,所谓内排序就是可以在内存中完成的排序。RAM的访问速度大约是磁盘的25万倍,我们当然希望如果可以的话都是内排来完成。但对于大数据集来说,内存是远远不够的,这时候就涉及到外排序的知识了。
外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装入内存的部分分别把每一部分调入内存完成排序。然后,对已经排序的子文件进行归并排序。
一般来说外排序分为两个步骤:预处理和合并排序。首先,根据可用内存的大小,将外存上含有n个纪录的文件分成若干长度为t的子文件(或段);其次,利用内部排序的方法,对每个子文件的t个纪录进行内部排序。这些经过排序的子文件(段)通常称为顺串(run),顺串生成后即将其写入外存。这样在外存上就得到了m个顺串(m=[n/t])。最后,对这些顺串进行归并,使顺串的长度逐渐增大,直到所有的待排序的记录成为一个顺串为止。
预处理阶段最重要的事情就是选择初始顺串。通常使用的方法为置换选择排序,它是堆排序的一种变形,实现思路同STL的partial_sort。步骤如下:
(1)初始化堆
从磁盘读入M个记录放到数组RAM中;
设置堆末尾标准LAST=M-1;
建立最小值堆。
(2)重复以下步骤直到堆为空
把具有最小关键码值的记录Min也就是根节点送到输出缓冲区;
设R是输入缓冲区中的下一条记录,如果R的关键码大于刚刚输出的关键码值Min,则把R放到根节点,否则使用数组中LAST位置的记录代替根节点,并将刚才的R放入到LAST所在位置,LAST=LAST-1;
(3)重新排列堆,筛出根节点。
如果堆的大小是M,一个顺串的最小长度就是M个记录,因为至少原来在堆中的那些记录将成为顺串的一部分,如果输入时逆序的,那么顺串的长度只能是M,最好情况输入是正序的,有可能一次性就能把整个文件生成一个顺串,由此可见生成顺串的长度是大小不一的,但是每个顺串却是有序的,利用扫雪机模型能够得到平均顺串的长度为2M。
外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装入内存的部分,分别把每一部分调入内存完成排序。然后,对已经排序的子文件进行归并排序。
(1) 二路合并排序
二路合并是最简单的合并方法,合并的实现与内排序中的二路归并算法并无本质区别,下面通过具体例子,分析二路合并外部排序的过程。
有一个含有9000个纪录的文件需要排序(基于关键字)。假定系统仅能提供容纳1800个纪录的内存。文件在外存(如磁盘)上分块存储,每块600个纪录。外部排序的过程分为生成初始顺串和对顺串进行归并排序两个阶段。在生成初始顺串阶段,每次读入1800个纪录(即3段)待内存,采用内排序依次生成顺串依次写入外存储器中。
顺串生成后,就开一开始对顺串进行归并。首先将内存等分成3个缓冲区,每个缓冲区可容纳600个纪录,其中两个为输入缓冲区,一个为输出缓冲区,每次从外存读入待归并的块到输入缓冲区,进行内部归并,归并后的结果送入输出缓冲区,输出缓冲区的记录写满时再将其写入外存。若输入缓冲区中的纪录为空,则将待归并顺串中的后续块读入输入缓冲区中进行归并,直到待归并的两个顺串都已归并为止。重复上述的归并方法,由含有5块(每块上限1800个记录)的顺串经二路归并的一遍归并后生成含有3块(每块上限3600个记录)的顺串,再经过第二遍……第s遍(s=[ ],m为初始顺串的个数),生成含有所有记录的顺串,从而完成了二路归并外部排序。
对文件进行外部排序的过程中,因为对外存的读写操作所用的操作的时间远远超过在内存中产生顺串和合并所需的时间,所以常用对外存的读写操作所用的时间作为外部排序的主要时间开销。分析一下上述二路归并排序的对外存的读写时间。初始时生成5个顺串的读写次数为30次(每块的读写次数为2次)。
类似地,可得到二路、三路……多路合并方法。
(2) 多路替代选择合并排序
采用多路合并技术,可以减少合并遍数,从而减少块读写次数,加快排序速度。但路数的多少依赖于内存容量的限制。此外,多路合并排序的快慢还依赖于内部归并算法的快慢。
设文件有n个纪录,m个初始顺串,采用k路合并方法,那么合并阶段将进行遍合并。k路合并的基本操作是从k个顺换的第一个纪录中选出最小纪录(即关键字最小的纪录),把它从输入缓冲区移入输出缓冲区。若采用直接选择方式选择最小元,需要k-1次比较,遍合并共需n(k-1)=次比较。由于随k的增长而增长,则内部归并时间亦随k的增大而增大,这将抵消由于增大k而减少外存信息读写时间所得的效果。若在k个纪录中采用树形选择方式选择最小元,则选择输出一个最小元之后,只需从某叶到根的路径上重新调整选择树,即可选择下一个最小元。重新构造选择书仅用O( )次比较,于是内部合并时间O(n)=O( ),它与k无关,不再随k的增大而增大。
常见的有基于“败者树”的多路替代选择合并排序方法。
外归并排序法并不是唯一的外排序算法。另外还有外分配排序,其原理类似于内排序中的桶排序。在归并排序和桶排序之间存在数学上的某种对偶性。此外还有一些不耗费附加磁盘空间的原地排序算法。
计算机科学家吉姆·格雷的SortBenchmark网站用不同的硬件、软件环境测试了实现方法不同的多种外排序算法的效率。效率较高的算法具有以下的特征:
提高硬件速度
提高软件速度
考虑到外部排序涉及的待排序记录数量大,可以采取分治的思想(即先分解, 再递归求解, 然后合并) 将其划分成几段合适的待排序记录,然后对每一小段采用内部排序方法。换句话说,就是将外部排序转化为内部排序,所以为了进一步研究外部排序, 需先对内部排序进行深入的讨论。如果在排序过程中依据不同原则对内部排序方法进行分类,则大致可分为插入排序、冒泡排序、选择排序、归并排序和快速排序等5类。
通常, 排序记录存储具有如下3种形式:
1、待排序的一组记录存放在地址连续的一组存储单元上,它类似于线性表的顺序存储结
构,在序列中相邻的2个记录Rj 和R j+ 1 ( j = 1, 2,……, n - 1) 其存储位置也相邻。这种存储方式中,记录之间的次序关系由其存储的位置决定,排序通过移动记录来实现。
2、一组待排序记录存放在静态链表中,记录之间的次序关系由指针指示, 则实现排序不需要移动记录,仅需移动指针即可。
3、待排序记录本身存储在一组地址连续的存储单元内, 同时另设一个指示各个记录存储位
置的地址向量, 在排序过程中不移动记录本身,而移动地址向量中这些记录的 地址, 在排序结束之后再按照地址向量中的值调整记录的存储位置。