多路归并排序 外部排序算法
- 格式:docx
- 大小:16.23 KB
- 文档页数:3
ClickHouse排序规则1. 介绍ClickHouse是一个开源的分布式列式数据库管理系统,专为在线分析处理(OLAP)设计。
它具有高度的可扩展性和出色的性能,能够处理大规模数据集的快速查询和分析。
排序是数据库中一个重要的操作,而ClickHouse提供了丰富的排序功能,以满足不同场景下的排序需求。
本文将介绍ClickHouse的排序规则,包括排序的基本概念、排序方法、排序算法以及如何在ClickHouse中使用排序规则。
2. 排序的基本概念排序是指将一组数据按照某个特定的顺序重新排列的过程。
在数据库中,排序通常用于查询结果的排序、索引的构建以及优化查询等操作。
在ClickHouse中,排序是基于列进行的。
每个列都有一个特定的排序规则,用于指定列的排序方式。
排序规则决定了数据在列中的物理存储方式,以及在查询中的排序方式。
3. 排序方法ClickHouse支持多种排序方法,包括默认排序、自定义排序和多列排序。
3.1 默认排序默认排序是ClickHouse中最常用的排序方法。
它使用列的数据类型和字节序作为排序规则。
对于数字类型,按照数值的大小进行排序;对于字符串类型,按照字典序进行排序。
例如,对于以下查询语句:SELECT * FROM table ORDER BY column;其中,column是要排序的列。
如果column是数字类型,查询结果将按照数值的大小进行排序;如果column是字符串类型,查询结果将按照字典序进行排序。
3.2 自定义排序除了默认排序规则,ClickHouse还支持自定义排序规则。
自定义排序规则可以通过使用ORDER BY子句中的COLLATE关键字来指定。
例如,对于以下查询语句:SELECT * FROM table ORDER BY column COLLATE my_collation;其中,column是要排序的列,my_collation是自定义的排序规则。
外部排序处理大规模数据的外部存储排序算法在计算机科学中,外部排序是指对大规模数据进行排序时所采用的一种排序方式。
由于计算机内存的有限性,当数据量超过内存容量时,无法一次性加载到内存中进行排序。
因此,我们需要将数据分割成多个较小的块,在磁盘上进行排序,然后再将排序好的块逐个合并成最终有序的结果。
外部存储排序算法是一种用于处理大规模数据的高效排序算法,它充分利用了磁盘I/O的特性,以提高排序的效率和整体性能。
下面将介绍两种常见的外部存储排序算法:归并排序和多路归并排序。
一、归并排序归并排序是一种常见的排序算法,它也被广泛应用于外部存储排序中。
其基本思想是将待排序的数据划分为若干个子序列,分别进行内部排序,然后再将排好序的子序列进行合并,最终得到全局有序的结果。
归并排序的具体步骤如下:1. 将大规模数据划分成多个块并加载到内存中。
2. 对每个块进行内部排序,可以选择快速排序、堆排序等高效的排序算法。
3. 将排好序的块写入磁盘,同时将下一块数据加载到内存中。
4. 重复步骤2和步骤3,直到所有块都排序完毕。
5. 对排好序的块进行多路归并,生成最终的有序结果。
归并排序的时间复杂度为O(n log n),其中n表示待排序数据的总量。
它的优势在于适用于处理大规模数据,但由于需要频繁进行磁盘I/O,因此效率较低。
二、多路归并排序多路归并排序是一种改进版的归并排序算法,它能够同时合并多个有序的子序列,并生成一个更大的有序序列。
与传统的两路归并排序不同,多路归并排序可以合并超过两个的子序列。
多路归并排序的核心思想是使用最小堆来管理各个子序列的当前元素,每次从堆中选择最小的元素输出,并将其所在的子序列的下一个元素加入堆中。
通过不断地选择最小的元素,最终实现多路归并排序。
多路归并排序的具体步骤如下:1. 将大规模数据划分成多个块并加载到内存中。
2. 对每个块进行内部排序,可以选择快速排序、堆排序等高效的排序算法。
3. 将块的首个元素创建最小堆,并将最小堆中的元素输出到磁盘。
排序算法发展现状及未来趋势分析排序算法是计算机科学中一类非常重要的算法,其用于将一组数据按照指定的顺序进行排列。
根据不同的需求,排序算法可以采用不同的策略和技巧,在效率、稳定性和空间复杂度等方面有着显著的差异。
本文将对排序算法的发展现状进行分析,探讨其未来的发展趋势。
目前,排序算法主要分为内部排序和外部排序两大类。
内部排序是指可以在内存中完成排序的算法,而外部排序则是用于处理数据量超过内存容量的数据排序。
在讨论排序算法的发展现状时,我们主要关注内部排序算法。
最经典的排序算法包括冒泡排序、插入排序、选择排序和快速排序等。
冒泡排序和插入排序基于相邻元素的比较和交换,效率较低,但实现简单。
选择排序则通过每次选择最小(或最大)的元素进行排序,但其时间复杂度较高。
相比之下,快速排序是一种高效的排序方法,它通过选取一个基准元素,将数组划分为两部分进行递归排序,具有快速、原地排序的特点。
然而,随着数据规模和复杂性的增加,上述传统排序算法已经难以满足现实应用的需求。
因此,研究者们不断努力提出更加高效的排序算法。
通过改进和创新,现在已经提出了一系列优秀的排序算法,例如归并排序、堆排序和计数排序等。
归并排序是一种典型的分治策略,它将待排序的数据分成两部分,分别进行排序后再进行合并。
归并排序具有稳定性和可靠性,适用于各种不同规模的数据集。
堆排序是一种基于堆数据结构的排序算法,通过构建最大(或最小)堆实现排序。
堆排序具有原地排序的特点,且时间复杂度稳定为O(nlogn)。
计数排序则利用一个辅助数组来记录待排序元素的出现频率,然后根据频率进行排序。
计数排序适用于数据范围较小且元素重复率较高的情况,它的时间复杂度为O(n+k),其中k代表数据范围大小。
除了以上介绍的算法外,还有一些其他的排序算法也值得关注。
比如,基数排序、桶排序和外部排序等。
基数排序是一种非基于比较的排序算法,它将待排序元素从低位到高位依次进行排序,要求每个位元素必须属于有限集合。
排序问题求解技巧排序问题是计算机科学领域中非常重要的一类问题,通常涉及到对一组数据进行排列的操作。
在实际应用中,通常需要根据不同的需求和条件对数据进行排序,以方便后续的处理和分析。
下面将介绍一些排序问题求解的常见技巧。
1. 冒泡排序(Bubble Sort)冒泡排序是一种简单的排序算法,其基本思想是比较相邻的元素并交换位置,从而实现排序。
具体操作如下: - 从第一个元素开始依次比较相邻的两个元素,如果顺序不符合要求则交换位置,直到比较完所有相邻元素。
- 重复上述步骤,直到没有需要交换位置的元素。
冒泡排序的时间复杂度为O(n^2),空间复杂度为O(1)。
2. 插入排序(Insertion Sort)插入排序是一种简单直观的排序算法,其基本思想是将一个元素依次插入到已经排好序的部分中,从而实现排序。
具体操作如下:- 从第二个元素开始,将当前元素插入到已经排序的部分中的正确位置。
- 继续处理下一个元素,直到所有元素都被插入到正确位置为止。
插入排序的时间复杂度为O(n^2),空间复杂度为O(1)。
3. 选择排序(Selection Sort)选择排序是一种简单直观的排序算法,其基本思想是每次选出最小(或最大)的元素放到已经排序的部分的最后,从而实现排序。
具体操作如下:- 在未排序部分中,找到最小(或最大)的元素,并将其与未排序部分的第一个元素交换位置。
- 继续处理下一个未排序部分,直到所有元素都被排序为止。
选择排序的时间复杂度为O(n^2),空间复杂度为O(1)。
4. 快速排序(Quick Sort)快速排序是一种常用的高效率排序算法,其基本思想是通过递归地划分待排序的部分,使得左半段的所有元素都小于右半段的所有元素,并将基准元素放在正确的位置上。
具体操作如下:- 选择一个基准元素(通常是待排序部分的第一个元素),并将其放在正确的位置上。
- 将比基准元素小的元素放在左边,比基准元素大的元素放在右边,并分别对左右两个部分递归地进行快速排序。
关于多路归并排序外部排序⽐如⽂件内有1亿数据排序。
编程珠玑第⼀个case是有关⼀个技巧性解决外部排序问题的。
问题很巧妙的解决了,但⼀开始提到的利⽤归并排序进⾏外部排序的算法仍值得仔细探究⼀下,毕竟本科时学的不是很深⼊。
先来看内部排序中最简单的2路归并排序算法。
算法核⼼操作是将⼀维数组中前后相邻的两个有序序列归并为⼀个有序序列,给定数组中序列界限i、m、n,⽤2个下标变量分别从i和j=m+1开始逐个往后处理,先⽐较,⼩的写到结果序列的当前遍历下标k中,相应下标⾃增继续⽐较直到某个序列的下标⾛到边界,再将另外⼀个序列的剩余元素拷贝到结果序列中。
算法可⽤递归或递推实现,从相邻的两两元素开始不断调⽤上⾯的核⼼操作组成较长有序序列直到完成整个序列。
算法进⾏⼀趟归并就得到⼀个局部有序的完整新序列,n个元素共需要log2n趟归并,每趟完成⽐较操作n次(1次得到序列的1个值),得到的新序列写到结果序列空间中,下⼀趟之前要先将结果序列复制⼀份到临时空间,下⼀趟归并在临时空间上进⾏。
因此时间复杂度nlog2n,空间上除了原始序列空间n、结果序列空间n,还需要辅助临时空间n。
接下来看外部排序。
外部排序指的是⼤⽂件的排序,即待排序的记录存储在外存储器上,待排序的⽂件⽆法⼀次装⼊内存,需要在内存和外部存储器之间进⾏多次数据交换,以达到排序整个⽂件的⽬的。
外部排序最常⽤的算法是多路归并排序,即将原⽂件分解成多个能够⼀次性装⼊内存的部分,分别把每⼀部分调⼊内存完成排序。
然后,对已经排序的⼦⽂件进⾏多路归并排序。
多路归并排序算法在常见数据结构书中都有涉及。
从2路到多路(k路),增⼤k可以减少外存信息读写时间,但k个归并段中选取最⼩的记录需要⽐较k-1次,为得到u个记录的⼀个有序段共需要(u-1)(k-1)次,若归并趟数为s次,那么对n个记录的⽂件进⾏外排时,内部归并过程中进⾏的总的⽐较次数为s(n-1)(k-1),也即(向上取整)(logkm)(k-1)(n-1)=(向上取整)(log2m/log2k)(k-1)(n-1),⽽(k-1)/log2k随k增⽽增因此内部归并时间随k增长⽽增长了,抵消了外存读写减少的时间,这样做不⾏,由此引出了“败者树”treeof loser的使⽤。
外部排序归并排序与多路归并外部排序是一种针对大规模数据进行排序的算法,常用的外部排序算法包括归并排序和多路归并。
本文将对外部排序、归并排序和多路归并进行详细介绍和比较。
一、外部排序外部排序是指当数据量过大,无法一次性加载到内存中进行排序时,需要使用外部存储器(如硬盘)进行排序的一种算法。
外部排序主要包括两个阶段:排序阶段和归并阶段。
排序阶段将大数据划分为若干个能够加载到内存的小块,对每个小块进行排序;归并阶段将排好序的小块按照规定的方式进行合并和排序,最终得到整个数据的有序结果。
二、归并排序归并排序是一种分治策略的排序算法,它将待排序的数据分成若干个小块,然后分别对每个小块进行排序,最后再将排序好的小块进行合并,得到整个数据的有序结果。
归并排序采用递归的思想,先对每个小块进行排序,再进行小块的合并。
1. 归并排序算法步骤:- 将待排序的数据分成两个子问题,分别对左右两个子问题进行归并排序;- 当左右两个子问题均排序完成后,将两个有序子数组进行合并得到最终的有序结果。
2. 归并排序的优缺点:- 优点:稳定,时间复杂度为O(nlogn),适用于大规模数据的排序;- 缺点:需要额外的空间进行数据的合并,空间复杂度为O(n)。
三、多路归并多路归并是对归并排序的改进和扩展,它将归并排序的两路归并扩展为多路归并。
多路归并可以减少磁盘I/O的次数,提高排序效率。
1. 多路归并算法步骤:- 将待排序的数据划分为多个块;- 对每个块进行排序,得到有序子块;- 将有序子块进行多路归并,得到最终的有序结果。
2. 多路归并的优缺点:- 优点:减少磁盘I/O次数,提高排序效率;- 缺点:增加了算法的复杂性,不适用于小规模数据的排序。
四、归并排序与多路归并的对比归并排序和多路归并都是外部排序的经典算法,它们在处理大规模数据时具有一定的优势。
但在具体应用时,需要根据实际情况选择合适的算法。
1. 时间复杂度:归并排序和多路归并的时间复杂度都为O(nlogn),其中n为待排序数据的大小。
常见的算法有哪些算法是计算机科学的基础,通过一系列的操作步骤将输入转换为输出。
算法的好坏直接影响着计算机程序执行效率和程序的优化。
在实际的编程中,我们常常需要根据具体问题应用不同的算法,以达到最佳的计算效果。
本篇论文将对常见的算法进行概述和分析。
一、排序算法排序是计算机科学中的一个非常基础的问题,其作用不仅限于程序的实现,也包括了整个数据库、计算机图形学和人工智能等领域。
排序算法可以分为内部排序和外部排序两大类。
1、内部排序内部排序指所有排序操作在内存中完成,不需要额外的存储空间。
常见的内部排序算法包括:(1)冒泡排序冒泡排序是一种非常简单但效率较低的排序算法,其基本思想是通过不断的交换相邻元素,将最大值逐渐推向数组的末端。
该算法的时间复杂度为O(n^2)。
(2)选择排序选择排序是一种效率相对较高的排序算法,其基本思想是在每一轮遍历中选择最小元素,与前面元素进行交换。
该算法的时间复杂度为O(n^2)。
(3)插入排序插入排序是一种效率稍高的排序算法,其基本思想是将数组不断地插入到已排序的数组中。
该算法的时间复杂度为O(n^2)。
(4)快速排序快速排序是一种性能最优的排序算法之一,其基本思想是通过不断地划分数据集合,将问题规模逐渐缩小。
该算法的时间复杂度为O(nlogn)。
(5)归并排序归并排序是一种稳定而有效的排序算法,其基本思想是将数据按照一定的规则划分,然后将分开的数据不断合并。
该算法的时间复杂度为O(nlogn)。
2、外部排序外部排序指在内存空间有限的情况下,通过硬盘或其他外部存储设备进行排序。
常见的外部排序算法包括多路归并排序、败者树排序、平衡树排序等。
二、搜索算法搜索算法是一种通过在数据集合中查找特定元素的算法。
在计算机科学中,搜索算法通常涉及一组操作,以在数据集合中查找目标。
常见的搜索算法包括:1、线性搜索线性搜索也称为顺序搜索,其基本思想是依次遍历数据集合,直到查找到特定元素为止。
该算法的时间复杂度为O(n)。
解决大规模数据问题的外部排序算法外部排序是一种处理大规模数据的排序算法,由于内存限制,无法将所有数据加载到内存中进行排序。
所以,外部排序利用磁盘空间来进行数据的划分和排序,以解决大规模数据问题。
本文将介绍一种常用的外部排序算法——多路归并排序。
一、多路归并排序概述多路归并排序是一种基于分治思想的排序方法,它将大规模数据划分为多个小块,并在磁盘上对这些小块进行排序。
然后,再将排好序的小块进行归并,最终得到完整有序的数据。
多路归并排序多用于外排序,其主要特点是能够处理大规模数据,并且排序效率较高。
二、多路归并排序的实现步骤1. 数据划分首先,将大规模数据划分为多个小块。
可以通过读取数据集并将其划分为大小相等的块,每个块的大小受到内存大小的限制。
2. 内部排序对每个小块进行内部排序,常用的内部排序算法包括快速排序、归并排序、堆排序等。
选择合适的内部排序算法,将小块排序后存放在磁盘中。
3. 多路归并将排好序的小块进行多路归并。
多路归并即将多个有序的序列合并为一个有序序列。
在多路归并中,可以借助最小堆等数据结构,每次从多个序列中选择最小的元素,加入到有序序列中。
4. 写出结果将最终得到的有序序列写出到磁盘文件中。
三、多路归并排序算法的优化1. 外部排序的前提是磁盘I/O的次数尽可能少,因此可以采用合适的数据结构来减少读写次数,如B+树等。
2. 利用多线程或多进程进行归并操作可以加快排序速度。
可以将原始数据划分为多个小块,并利用多个线程或进程分别对这些小块进行排序和归并。
3. 预读数据是提高排序效率的一个关键。
可以采用预读技术,提前将数据加载到内存缓冲区中,减少磁盘I/O的次数。
4. 考虑数据的分布情况进行数据划分。
如果数据是有序的,可以将其均匀划分到不同的小块中,以充分利用有序性。
四、总结多路归并排序是一种有效解决大规模数据问题的外部排序算法。
通过将数据划分为多个小块,并采用多路归并的方式进行排序,可以充分利用磁盘空间和减少磁盘I/O次数。
缫丝排序算法缫丝排序算法是一种语言无关的、稳定的排序算法,它允许在O(n)间复杂度内完成排序操作。
缫丝排序算法被认为是用于解决外部排序问题的最佳算法,特别是在处理非常大的数据集时,可以获得较高的性能。
本文主要介绍了缫丝排序算法,包括它的应用场景、原理、过程、优缺点以及针对其的改进方法。
缫丝排序算法的应用场景缫丝排序算法主要应用于外部排序问题。
外部排序又称为外排序,是对在外部存储器中的大型文件进行排序的一种算法。
一般情况下,它被用于排序数据量超过可存入内存中的最大值,因此无法通过内存中的排序算法来完成。
缫丝排序算法因其可以在外部存储上实现相对较快的排序速度,因此被广泛应用于解决外部排序问题。
缫丝排序算法的原理缫丝排序算法是一种典型的分治算法,它是将原始文件分成相同大小的一系列小文件,通过多路归并排序将它们合并成一个排序文件。
缫丝排序算法的过程缫丝排序算法包括三个主要过程:(1)分解:将原始文件分解成小文件,以减少排序时间。
(2)排序:对每个小文件进行排序,使其满足排序要求。
(3)归并:多路归并排序,将每个小文件依次合并成一个排序文件。
缫丝排序算法的优缺点缫丝排序算法有一定的优点,它的平均时间复杂度是O(nlogn),在特定的情况下,缫丝排序算法的最坏情况时间复杂度也小于O (nlogn),这比其他排序算法更优秀。
此外,缫丝排序算法不会改变原始文件的相对顺序,因此它是稳定的排序算法。
但是,由于缫丝排序算法比内存排序算法慢得多,因此它不能用于处理内存中的数据文件,而只能用于处理外部存储器中的数据文件。
另外,由于缫丝排序算法需要额外的空间来保存小文件,因此可能会占用比较多的空间。
针对缫丝排序算法的改进方法由于缫丝排序算法的缺点,一些方法被提出来改进它的效率。
首先,可以在分解阶段增加剩余聚类来提高排序性能。
其次,可以使用多路多遍归并排序,以减少归并步骤中的IO读写次数,从而提高排序的速度。
此外,引入缓存技术和分布式技术也是一种提高排序效率的方法。
《排序专项训练》课标要求一、理解排序概念1. 排序定义:排序是指将一组无序的数据按照一定的顺序进行排列,使其具有某种特定的顺序。
2. 排序目的:排序的目的通常是为了方便数据的查找、处理和存储。
二、学习基本排序方法1. 冒泡排序:通过相邻元素之间的比较和交换,将较大的元素逐渐向后移动,从而实现排序。
2. 选择排序:每次从未排序的元素中选取最小(或最大)的元素,将其放到已排序序列的末尾(或开头),直到所有元素均排序完毕。
3. 插入排序:将未排序的元素逐个插入到已排序序列中,每次插入后,已排序序列保持有序。
4. 快速排序:通过选择一个基准元素,将比基准元素小的元素放在其左边,比基准元素大的元素放在其右边,然后递归地对左右两个子序列进行快速排序。
三、掌握高级排序方法1. 归并排序:将待排序的序列划分为若干个子序列,每个子序列都是有序的,然后将这些子序列合并成一个有序的序列。
2. 堆排序:利用堆这种数据结构进行排序,通过构建最大堆或最小堆,然后依次取出堆顶元素,再重新调整堆结构,直到所有元素均排序完毕。
3. 希尔排序:通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。
四、了解排序算法的应用1. 内部排序:对存储在计算机内存中的数据按照某种顺序进行排序。
2. 外部排序:对存储在计算机外部存储器中的数据进行排序,通常采用多路归并排序算法。
3. 分布式排序:在分布式系统中对数据进行排序,需要考虑到网络通信和分布式存储等因素。
4. 实时排序:在处理大量实时数据时,需要快速地对数据进行排序,以便及时做出决策。
5. 数据库中的排序:在数据库中查询数据时,需要对数据进行排序以满足特定的需求。
五、培养思维能力1. 逻辑思维:通过学习不同类型的排序算法,培养逻辑思维和问题解决能力。
2. 算法分析能力:通过对不同排序算法的分析和比较,提高算法分析能力。
3. 创新思维:鼓励学生在学习过程中提出自己的想法和创新思路,培养创新思维和解决问题的能力。
排序方法有哪几种在日常生活和工作中,我们经常需要对一些事物或者数据进行排序,以便更好地理清思路或者找到需要的信息。
而对于排序方法,我们常常会采用多种不同的方式来进行排序。
下面就让我们来了解一下,排序方法究竟有哪几种。
首先,我们可以根据排序的对象来分类排序方法。
一种常见的分类方式是将排序方法分为内部排序和外部排序。
内部排序是指所有的排序操作都可以在内存中完成的排序方法,而外部排序则是指需要借助外部存储设备(如磁盘)来完成的排序方法。
在内部排序中,常见的排序方法包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。
而在外部排序中,常见的排序方法包括多路归并排序、置换-选择排序等。
其次,我们还可以根据排序的原理来分类排序方法。
根据排序原理的不同,排序方法可以分为比较排序和非比较排序。
比较排序是指通过比较排序元素的大小来确定排序顺序的方法,而非比较排序则是指不通过比较元素的大小来确定排序顺序的方法。
在比较排序中,常见的排序方法包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。
而在非比较排序中,常见的排序方法包括计数排序、桶排序、基数排序等。
另外,我们还可以根据排序的稳定性来分类排序方法。
稳定性是指当排序的元素中存在相等的元素时,排序后它们的相对位置是否发生改变。
稳定排序是指如果a原本在b前面,而a=b,排序之后a仍然在b的前面,不稳定排序则是指如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面。
在稳定排序中,常见的排序方法包括冒泡排序、插入排序、归并排序等。
而在不稳定排序中,常见的排序方法包括选择排序、快速排序等。
最后,我们还可以根据排序的时间复杂度和空间复杂度来分类排序方法。
时间复杂度是指执行算法所需要的计算工作量,而空间复杂度是指执行算法所需要的内存空间。
根据时间复杂度和空间复杂度的不同,排序方法可以分为最好情况时间复杂度、最坏情况时间复杂度、平均情况时间复杂度和空间复杂度。
在实际应用中,我们需要根据具体的需求来选择合适的排序方法,以便在时间和空间上都能够得到较好的效果。
关于多路归并排序外部排序败者树技术积累2009-11-24 21:52:06 阅读453 评论0 字号:大中小
编程珠玑第一个case是有关一个技巧性解决外部排序问题的。
问题很巧妙的解决了,但一开始提到的利用归并排序进行外部排序的算法仍值得仔细探究一下,毕竟本科时学的不是很深入。
先来看内部排序中最简单的2路归并排序算法。
算法核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列,给定数组中序列界限i、m、n,用2个下标变量分别从i和j=m+1开始逐个往后处理,先比较,小的写到结果序列的当前遍历下标k中,相应下标自增继续比较直到某个序列的下标走到边界,再将另外一个序列的剩余元素拷贝到结果序列中。
算法可用递归或递推实现,从相邻的两两元素开始不断调用上面的核心操作组成较长有序序列直到完成整个序列。
算法进行一趟归并就得到一个局部有序的完整新序列,n个元素共需要log2n趟归并,每趟完成比较操作n次(1次得到序列的1个值),得到的新序列写到结果序列空间中,下一趟之前要先将结果序列复制一份到临时空间,下一趟归并在临时空间上进行。
因此时间复杂度nlog2n,空间上除了原始序列空间n、结果序列空间n,还需要辅助临时空间n。
接下来看外部排序。
外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。
外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装入内存的部分,分别把每一部分调入内存完成排序。
然后,对已经排序的子文件进行多路归并排序。
多路归并排序算法在常见数据结构书中都有涉及。
从2路到多路(k路),增大k可以减少外存信息读写时间,但k个归并段中选取最小的记录需要比较k-1次,为得到u个记录的一个有序段共需要(u-1)(k-1)次,若归并趟数为s次,那么对n个记录的文件进行外排时,内部归并过程中进行的总的比较次数为s(n-1)(k-1),也即(向上取整)(logkm)(k-1)(n-1)=(向上取整)(log2m/log2k)(k-1)(n-1),而(k-1)/log2k随k增而增因此内部归并时间随k增长而增长了,抵消了外存读写减少的时间,这样做不行,由此引出了“败者树”tree of loser的使用。
在内部归并过程中利用败者树将k个归并段中选取最小记录比较的次数降为(向上取整)(log2k)次使总比较次数为(向上取整)(log2m)(n-1),与k无关。
败者树是完全二叉树,因此数据结构可以采用一维数组。
其元素个数为k个叶子结点、k-1个比较结点、1个冠军结点共2k个。
ls[0]为冠军结点,ls[1]--ls[k-1]为比较结点,ls[k]--ls[2k-1]为叶子结点(同时用另外一个指针索引b[0]--b[k-1]指向)。
另外bk为一个附加的辅助空间,不属于败者树,初始化时存着MINKEY的值。
多路归并排序算法的过程大致为:首先将k个归并段中的首元素关键字依次存入
b[0]--b[k-1]的叶子结点空间里,然后调用CreateLoserTree创建败者树,创建完毕之后最小的关键字下标(即所在归并段的序号)便被存入ls[0]中。
然后不断循环:把ls[0]所存最小关键字来自于哪个归并段的序号得到为q,将该归并段的首元素输出到有序归并段里,然后把下一个元素关键字放入上一个元素本来所在的叶子结点b[q]中,调用Adjust顺着b[q]这个叶子结点往上调整败者树直到新的最小的关键字被选出来,其下标同样存在ls[0]中。
循环这个操作过程直至所有元素被写到有序归并段里。
伪代码如下:
void Adjust(LoserTree &ls, int s)
/*从叶子结点b[s]到根结点的父结点ls[0]调整败者树*/
{ int t, temp;
t=(s+K)/2; /*t为b[s]的父结点在败者树中的下标,K是归并段的个数*/
while(t>0) /*若没有到达树根,则继续*/
{ if(b[s]>b[ls[t]]) /*与父结点指示的数据进行比较*/
{ /*ls[t]记录败者所在的段号,s指示新的胜者,胜者将去参加更上一层的比较*/
temp=s;
s=ls[t];
ls[t]=temp;
}
t=t/2; /*向树根退一层,找到父结点*/ }
ls[0]=s; /*ls[0]记录本趟最小关键字所在的段号*/
}
void K_merge( int ls[K])
/*ls[0]~ls[k-1]是败者树的内部比较结点。
b[0]~b[k-1]分别存储k个初始归并段的当前记录*/ /*函数Get_next(i)用于从第i个归并段读取并返回当前记录*/
{ int b[K+1),i,q;
for(i=0; i<K;i++)
{ b[i]=Get_next(i); /*分别读取K个归并段的第一个关键字*/ }
b[K]=MINKEY; /*创建败者树*/
for(i=0; i<K ; i++) /*设置ls中的败者初值*/
ls[i]=K;
for(i=K-1 ; i>=0 ; i--) /*依次从b[K-1]……b[0]出发调整败者*/ Adjust(ls , i); /*败者树创建完毕,最小关键字序号存入ls[0] while(b[ls[0]] !=MAXKEY )
{ q=ls[0]; /*q为当前最小关键字所在的归并段*/ prinftf("%d",b[q]);
b[q]=Get_next(q);
Adjust(ls,q); /*q为调整败者树后,选择新的最小关键字*/ }
}
最后,对使用多路归并排序来进行外部排序的过程大致描述一下:根据有限的内存资源将大文件分为L个段,然后依次将这L个段读入内存并利用高效的内部排序算法对每个段进行排序,排序后的结果即为初始有序归并段直接写入外存文件。
内部排序时要选择合适的排序算法,并且要考虑到内部排序需要的辅助空间以及有限的内存空间来决定究竟要把大文件分为几个段。
接下来选择合适的路数k对这L个归并段进行多路归并排序,每一趟归并使k 个归并段变为1个较大归并段写入文件,反复几趟归并后得到整个有序的文件。
在多路归并过程中,内存空间只需要维护一个大小为2k的败者树,数据取、放都是对应外存的读写,这样的话一次把一大块数据读入内存、把内存中排好的一大块数据写入文件比较省时,不知这个需要程序员编程安排还是OS能通过虚拟页面文件直接帮忙做到。
找出计算机组成原理的课本回顾下发现,自认为用虚拟页面文件管理解决这个问题完全是风马牛不相及的。
段页式虚拟存储是将程序的逻辑空间以段页式来管理,而要排序的文件不属于程序本身的逻辑空间。
实际上,这个问题应该从磁盘本身提供的高速缓存方面来考虑。
现在磁盘一般都有几M 到十几M的高速缓存,利用数据访问的空间局部性和时间局部性规则,使用预读策略,一次性将一块数据读入高速缓存,再次读写时则先检查cache中是否能够命中,如能命中则不需去盘片上读。
若cache空间不足以提高读写速率,则需要程序员编写程序将大块数据读入写出。