如何排序10G个元素
我们内存没有这么大,我们没办法放进大数组里面
有一部分放在数组里面,有一部分放在外面,就是硬盘或者网络上其他节点,
就是所谓的外部排序
扩展的归并排序
我们数据不要分左右两端,我们分很多段
每一段给一个节点进行排序,每一段他的数据量我们可以控制小到放在内存里可以放得下
那这个节点就可以做普通的排序,快速归并
拍出来之后每个节点都是有序序列,每个有序序列都归并节点
我们把大数据且分到每一个节点上,每一个节点做一个内部排序然后进行节点归并
如何实现?
首先切分很简单,500M1G看看有多大
切分玩之后节点内部排序这个很简单,放在一个数组里调用库函数就拍出来了
因此整个算法的关键就在归并节点的实现
k路归并,我们发现我们有很多串有序数据传过来
2,1,3,4从上到下分别是从上到下,2,1,3,4如何选取最小的数
k路要比较k次
我们要运用堆的数据结构
堆是一棵树,是一个完全二叉树(从上到下,从左到右都是铺满的)
树根1拿掉就是2,2就是树根,2拿到就是2的左子树根
堆里面找最小元素只要logn
我们不需要实现是PriorityQueue
实际上是优先队列
一个个push进去,pop出来就是最小的1,后面2,3,4
首先一上来先把2,1,3,4push进去
然后不断的pop,pop一个元素补一个元素,当我们做完之后我们就做完了,每次pop补一个都是log运算量
那我们归并节点这样做就完成了吗?
还有一个思考的地方
我们任然是10G的数据
我们归并节点的数据还是不够
这些数据不能全放在内存里,归并节点到底要放多少数据在内存里呢
我们只要把每个数据源的最小放在内存里就可以了
我们只要把每个数据源最小的元素,我们只要把2134放在内存里
把1拿掉我们要把后面的读出来,
数据源是硬盘建,也可能是网络传输
我们这个读取查询过程会很慢,效率很低
因此我们通常做法是防缓存
我们从每个数据源读取他最小的一批数据放在内存里面,我们这例子里面放在
这样我们就好做了,我们把1拿出来,我么吧2,3拿出来
我们发现归并节点非常复杂
这个归并程序编写是很复杂
就要使用Iterable<T>接口
可以不断获得元素的能力
他是一个接口没有实现,元素存储/获取方式被抽象,与归并节点无关
不需要考虑元素怎么存储怎么获取
Iterable<T> merge(List<Iterable<T>> sortedData);
会变成这样,一串Iterable<T>
这就是被各节点被分别拍完序的数据
但是第一个一个元素还是一组一组输出的,这个不是merge考虑的
merge是负责出Iterable<T>
他可以考虑,我们是不是可以缓存多少以后一口气输出到文件
架在内存和网络文件的层次结构上面的,
归并数据源来自Iterable<T>.next()
我们归并节点怎么做呢,我们一上来先从每一个数据源调用最小的源,然后加到prorityto里面去
接下俩从prorityto pop元素,找到pop的数据源,获得下一个元素,下一个元素是怎么来的?
如果换冲区空那么读取下一批元素放入缓冲区,
给出换出区还没被输出的最小元素
这样有什么好处?
首先我们只需要对每个数据调用next
数据源自己他很灵活
可配置项:缓冲区大小,如何读取下一个元素
缓冲区大小不知道我们很难确定我们可以给他5K,10K或者1M两M都可以
如何读取下一批也可以配置的
下一个元素可能在文件没可能在网络
读文件和读网络都是读,这样一来我们归并函数只要写一遍,以后又可以用在文件上又可以用在网络上
这就是我们外部排序