经典算法题14-外排序
来源:互联网 发布:我的世界手机版生存js 编辑:程序博客网 时间:2024/06/06 15:40
引入
我们要处理一个大文件,对其中的数值排序,一般我们想到的方法就是用排序算法,像快速排序、归并排序、选择排序、堆排序、冒泡排序等。但是这些排序算法使用的前提是需要把数据读入到内存,现在大文件太大,内存装不下,如何处理?
这时我们就要用外排序(External sorting)。
介绍
外排序是指能够处理极大量数据的排序算法。归并(merge)排序算法中用到了分治思想,一个大问题我们可以采取分而治之,各个突破,当子问题解决了,大问题也就搞定了。外排序也采用的是一种“排序-归并”的策略。在排序阶段,先读入能放在内存中的数据量,将其排序输出到一个临时文件,依此进行,将待排序数据组织为多个有序的临时文件。然后在归并段阶将这些临时文件组合为一个大的有序文件,也即完成排序结果。过程如下图所示。
图中这里有个batch容器,这个容器我是基于性能考虑的,当batch=n时,我们定时刷新到文件中,保证内存有足够的空间。
外排序的一个例子是外归并排序(External merge sort),它读入一些能放在内存内的数据量,在内存中排序后输出为一个顺串(即是内部数据有序的临时文件),处理完所有的数据后再进行归并,这样就得到了完整文件的排序结果。维基百科(外排序)有这个简单实例,可以参考。
当然可以用其他常规排序方式(如快速排序、堆排序、归并排序等方法)在内存中完成小文件的排序。
简而言之:外排序实现是使用堆来生成若干个顺串,然后使用多路归并算法来生成最终排序好的内容。
败者树,赢者树
胜者树与败者树是完全二叉树。就像是参加比赛一样,每个选手有不同的实力,两个选手PK,实力决定胜负,晋级下一轮,经过几轮之后,就能得到冠军。
胜者树和败者树可以在log(n)的时间内找到最值,但是如果只是找最值,有点大材小用了,中间节点记录的标号就没有意义了。其意义在于,任何一个叶子节点的值改变后,利用中间节点的信息,还是能够快速的找到最值。以后细讲他们的实现。
外排序编码
// recursive method to merge the lists until we are left with a // single merged list private File process(ArrayList<File> list) throws IOException { if (list.size() == 1) { return list.get(0); } ArrayList<File> inter = new ArrayList<File>(); for (Iterator<File> itr = list.iterator(); itr.hasNext(); ) { File one = itr.next(); if (itr.hasNext()) { File two = itr.next(); inter.add(merge(one, two)); } else { return one; } } return process(inter); } /** * Splits the original file into a number of sub files. */ private ArrayList<File> split(File file) throws IOException { ArrayList<File> files = new ArrayList<File>(); int[] buffer = new int[BUFFER_SIZE]; FileInputStream fr = new FileInputStream(file); boolean fileComplete = false; while (!fileComplete) { int index = buffer.length; for (int i = 0; i < buffer.length && !fileComplete; i++) { buffer[i] = readInt(fr); if (buffer[i] == -1) { fileComplete = true; index = i; } } if (buffer[0] > -1) { Arrays.sort(buffer, 0, index); File f = new File("set" + new Random().nextInt()); FileOutputStream writer = new FileOutputStream(f); for (int j = 0; j < index; j++) { writeInt(buffer[j], writer); } writer.close(); files.add(f); } } fr.close(); return files; } /** * Merges two sorted files into a single file. * * @param one * @param two * @return * @throws IOException */ private File merge(File one, File two) throws IOException { FileInputStream fis1 = new FileInputStream(one); FileInputStream fis2 = new FileInputStream(two); File output = new File("data/merged" + new Random().nextInt()); FileOutputStream os = new FileOutputStream(output); int a = readInt(fis1); int b = readInt(fis2); boolean finished = false; while (!finished) { if (a != -1 && b != -1) { if (a < b) { writeInt(a, os); a = readInt(fis1); } else { writeInt(b, os); b = readInt(fis2); } } else { finished = true; } if (a == -1 && b != -1) { writeInt(b, os); b = readInt(fis2); } else if (b == -1 && a != -1) { writeInt(a, os); a = readInt(fis1); } } os.close(); return output; }
具体完整的代码见我的github。
https://github.com/shibing624/BlogCode/blob/master/src/main/java/xm/math/sort/extsort/ExternalSorter.java
结果
Redis实现
现在有了Redis做key-value处理,可以直接把大文件的数据加载到redis,使用其自带的排序,也可以实现排序功能,而且速度超快,下面的代码是用jedis实现了该功能。
package xm.nosql;import redis.clients.jedis.Jedis;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.InputStreamReader;import java.util.List;/** * @author xuming */public class SortDemo { public static void main(String[] args) { Jedis jedis; jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); //jedis 排序 //注意,此处的rpush和lpush是List的操作。是一个双向链表(但从表现来看的) jedis.del("b");//先清除数据,再加入数据进行测试 jedis.rpush("b", "1"); jedis.lpush("b", "6"); jedis.lpush("b", "3"); jedis.lpush("b", "9"); try { File f = new File("data/test_sort.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), "utf-8")); String readline; while ((readline = br.readLine()) != null) { jedis.lpush("b", readline.trim()); } br.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println(jedis.sort("b")); //[1, 3, 6, 9] //输入排序后结果 List<String> set = jedis.sort("b"); set.forEach(System.out::println); }}
具体完整的代码见我的github。https://github.com/shibing624/BlogCode/blob/master/src/main/java/xm/nosql/SortDemo.java
结果
- 经典算法题14-外排序
- 经典算法题每日演练——第十八题 外排序
- 经典算法题每日演练——第十八题 外排序
- 经典算法题每日演练——第十八题 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- 外排序
- shell脚本不使用if语句进行选择的脚本
- CSDN-markdown编辑器语法格式留存
- Scala可变长度参数
- 第十四周项目1 验证折半算法
- Eclipse常用快捷键
- 经典算法题14-外排序
- AngularJS入门心得1——directive和controller如何通信
- Json 返回timestamp 格式 js格式化年月日 时分
- 你是一名合格的软件工程师吗?
- Java数据结构与算法
- Android接入高德SDK开发(一)前奏----环境搭建
- html——影响文档流属性详解
- LEDataStream 下载地址
- Maven多模块项目构建