本文转自https://www.cnblogs.com/huangxincheng/archive/2012/12/19/2824943.html  

说到排序,大家第一反应基本上是内排序,是的,算法嘛,玩的就是内存,然而内存是有限制的,总有装不下的那一天,此时就可以来玩玩

外排序,当然在我看来,外排序考验的是一个程序员的架构能力,而不仅仅局限于排序这个层次。

 

一:N路归并排序

1.概序

    我们知道算法中有一种叫做分治思想,一个大问题我们可以采取分而治之,各个突破,当子问题解决了,大问题也就KO了,还有一点我们知道

内排序的归并排序是采用二路归并的,因为分治后有LogN层,每层两路归并需要N的时候,最后复杂度为NlogN,那么外排序我们可以将这个“二”

扩大到M,也就是将一个大文件分成M个小文件,每个小文件是有序的,然后对应在内存中我们开M个优先队列,每个队列从对应编号的文件中读取

TopN条记录,然后我们从M路队列中各取一个数字进入中转站队列,并将该数字打上队列编号标记,当从中转站出来的最小数字就是我们最后要排

序的数字之一,因为该数字打上了队列编号,所以方便我们通知对应的编号队列继续出数字进入中转站队列,可以看出中转站一直保存了M个记录,

当中转站中的所有数字都出队完毕,则外排序结束。如果大家有点蒙的话,我再配合一张图,相信大家就会一目了然,这考验的是我们的架构能力。

外排序(归并排序算法)

 

图中这里有个Batch容器,这个容器我是基于性能考虑的,当batch=n时,我们定时刷新到文件中,保证内存有足够的空间。

 

扩展

leveldb应用

在 LevelDB 数据库中高层数据下沉到低层时需要经历一次 Major Compaction,将高层文件的有序键值对和低层文件的多个有序键值对进行归并排序。磁盘多路归并排序算法的输入是来自多个磁盘文件的有序键值对,在内存中将这些文件的键值对进行排序,然后输出到一到多个新的磁盘文件中。

 

外排序(归并排序算法)

 

多路归并排序在大数据领域也是常用的算法,常用于海量数据排序。当数据量特别大时,这些数据无法被单个机器内存容纳,它需要被切分位多个集合分别由不同的机器进行内存排序(map 过程),然后再进行多路归并算法将来自多个不同机器的数据进行排序(reduce 过程),这是流式多路归并排序,为什么说是流式排序呢,因为数据源来源于网络套接字。

 

外排序(归并排序算法)

 

多路归并排序的优势在于内存消耗极低,它的内存占用和输入文件的数量成正比,和数据总量无关,数据总量只会线性正比影响排序的时间。

下面我们来亲自实现一下磁盘多路归并算法,为什么是磁盘,因为它的输入来自磁盘文件。

算法思路

我们需要在内存里维护一个有序数组。每个输入文件当前最小的元素作为一个元素放在数组里。数组按照元素的大小保持排序状态。

 

外排序(归并排序算法)

 

接下来我们开始进入循环,循环的逻辑总是从最小的元素下手,在其所在的文件取出下一个元素,和当前数组中的元素进行比较。根据比较结果进行不同的处理,这里我们使用二分查找算法进行快速比较。注意每个输入文件里面的元素都是有序的。

1. 如果取出来的元素和当前数组中的最小元素相等,那么就可以直接将这个元素输出。再继续下一轮循环。不可能取出比当前数组最小元素还要小的元素,因为输入文件本身也是有序的。

 

外排序(归并排序算法)

 

2. 否则就需要将元素插入到当前的数组中的指定位置,继续保持数组有序。然后将数组中当前最小的元素输出并移除。再进行下一轮循环。

 

外排序(归并排序算法)

 

3. 如果遇到文件结尾,那就无法继续调用 next() 方法了,这时可以直接将数组中的最小元素输出并移除,数组也跟着变小了。再进行下一轮循环。当数组空了,说明所有的文件都处理完了,算法就可以结束了。

 

外排序(归并排序算法)

 


值得注意的是,数组中永远不会存在同一个文件的两个元素,如此才保证了数组的长度不会超过输入文件的数量,同时它也不会把没有结尾的文件挤出数组导致漏排序的问题。

 

 

 

相关文章: