本文是学习 july博客中的如何给 10^7个数据量的磁盘文件排序,这一文章的学习文章。
问题:
输入:给定一个文件,里面有 最多n个不重复的正整数,其中每个数都小于等于n, n= 10^7
输出:得到按小到大升序排列的包含所有输入的整数的列表
条件:1M内存空间,时间5分钟以下,10s最佳
分析:
解法1 : 编程猪机上说的 位图法
java代码实现如下:
由于1 M 有 8 388 068 bit,代表放不下,所以要分开两部分。
当中需要使用java bitset类 , 为什么要用BitSet
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.BitSet;
public class bitsetuse {
static int max_each_scan = 5000000;
public static void main(String[] args) throws Exception{
BitSet bs = new BitSet(max_each_scan);
BufferedReader bis = new BufferedReader(new FileReader("dataMy.txt"));
String str=null;
while((str=bis.readLine())!=null){
String[] arrStr = str.split(" ");
int[] arrInt = new int[arrStr.length];
for(int i=0;i<arrStr.length;i++){
// 转为 整形
arrInt[i] = Integer.parseInt(arrStr[i]);
// 如果是 小于 max_each_scan set
if(arrInt[i]<max_each_scan){
bs.set(arrInt[i],true);
}
}
}
String path = "/home/administrator/workspace/HelloWorld/dataoutputMy.txt";
File f = new File(path);
BufferedWriter bw = new BufferedWriter(new FileWriter(f));
for(int i=0;i < max_each_scan;i++){
if(bs.get(i)){
System.out.print(i+" ");
bw.write(i+" ");
}
}
bs.clear();
bw.flush();
bis.close();
bis = new BufferedReader(new FileReader("dataMy.txt"));
while((str=bis.readLine())!=null){
String[] arrStr = str.split(" ");
int[] arrInt = new int[arrStr.length];
for(int i=0;i<arrStr.length;i++){
arrInt[i] = Integer.parseInt(arrStr[i]);
// 如果是 大于 max_each_scan的整形,
if(arrInt[i] >= max_each_scan){
bs.set(arrInt[i]-max_each_scan,true);
}
}
}
System.out.println();
for(int i=0;i < max_each_scan;i++){
if(bs.get(i)){
System.out.print(i+max_each_scan+" ");
bw.write(i+max_each_scan+" ");
}
}
// 原来本来一直都忘记 flush或者close来刷新缓冲区
bw.flush();
bw.close();
}
}
在对于io的各种不认识情况下,算是了解和写好了代码。
今日学习,毕。
下面研究如何用java生成1000W不重复的数据量
由于是从C++的程序中改过来的,所以先要了解一些C++的method以及它们在java中的实现
rand()在每次运行的时候都是和上一次相同,srand就是用来更改rand的随机数种子。
package javaio;
import java.util.Random;
public class javarandom {
/**
* @param args
*/
public static void main(String[] args) {
// 下面程序是获得指定范围内的重复随机数
int[] intRet = new int[6];
int intRd = 0; //存放随机数
int count = 0; //记录生成的随机数个数
int flag = 0; //是否已经生成过标志
while(count<6){
// 每次的种子都不一样,这个好像没必要,本来就不一样
Random rdm = new Random(System.currentTimeMillis());
// 获得一个指定范围的随机数,1到6的随机数
intRd = Math.abs(rdm.nextInt())%6+1;
//第一次不会执行
for(int i=0;i<count;i++){
// 从第二次开始,如果生成了重复的随机数目
// flag == 1 ,抛弃这个随机数,再生成一个,直到生成足够的随机数
if(intRet[i]==intRd){
flag = 1;
break;
}else{
flag = 0;
}
}
// 第一次执行后 count == 1
if(flag==0){
intRet[count] = intRd;
count++;
}
}
// 显示所有随机数
for(int t=0;t<6;t++){
System.out.println(t+"->"+intRet[t]);
}
}
}
以下是写入文件中的 一定范围内的不重复随机数的生成程序
package javaio;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.Random;
public class javarandom {
/**
* @param args
*/
public static void main(String[] args) throws Exception{
final int size = 1000;
// 下面程序是获得指定范围内的重复随机数
int[] intRet = new int[size];
int intRd = 0; //存放随机数
int count = 0; //记录生成的随机数个数
int flag = 0; //是否已经生成过标志
while(count<size){
// 每次的种子都不一样,这个好像没必要,本来就不一样
Random rdm = new Random(System.currentTimeMillis());
// 获得一个指定范围的随机数,1到6的随机数
intRd = Math.abs(rdm.nextInt())%size+1;
//第一次不会执行
for(int i=0;i<count;i++){
// 从第二次开始,如果生成了重复的随机数目
// flag == 1 ,抛弃这个随机数,再生成一个,直到生成足够的随机数
if(intRet[i]==intRd){
flag = 1;
break;
}else{
flag = 0;
}
}
// 第一次执行后 count == 1
if(flag==0){
intRet[count] = intRd;
count++;
}
}
// 显示所有随机数
// for(int t=0;t<size;t++){
// System.out.println(t+"->"+intRet[t]);
// }
BufferedWriter bs = new BufferedWriter(new FileWriter("dataRandom.txt"));
// 写入文件中
for(int t=0;t<size;t++){
bs.write(intRet[t]+" ");
if(t%100 == 0 && t!=0){
bs.write("\n");
}
//System.out.println(t+"->"+intRet[t]);
}
bs.close();
}
}
接下来就进行 多路归并
多路归并与一般归并的区别,一般归并是2路归并,多路归并是k路归并
归并的有序表有两个,叫做二路归并,归并的有序表有k个,称为k路归并
归并的算法分析:
1. 稳定性: 归并排序是一种稳定的排序。
2. 存储结构要求:可以顺序,也可以链表
3. 时间复杂度:无论是最好还是最环都是O(nlgn)
4. 空间复杂度:O(n) 不是就地排序
注意:若是用单链表做存储结构,很容易给出就地的归并排序
在链表排序中,归并排序很受欢迎,在基于比较的排序法中,时间复杂度为0(nlgn)的排序算法中唯一稳定的排序
多路归并的C++实现转为Java实现,开始:
对Java IO不熟悉,这段代码无法写出,惭愧。
总结:
位图和多路归并两种方案的时空间复杂度比较:
时间 | 空间 | |
位图 | O(N) | 0.625M |
多路归并 | O(Nlogn) | 1M |
多路归并的时间还要加上读写磁盘的时间,以上只是简化的时间复杂度
由于位图只能对只出现一次的特殊情况,所以虽然多路归并败者树比较浪费时间,但是当数据有重复的时候还是有用。程序是写不出了,可以看看败者树,熟悉理论。
bit-map的适用范围:
数据的快速查找,判重(这个相当重要,因为bit-map的不重复特性导致,去重复还可以选用hash),删除。
本章学习,毕,留下任务:多路归并理论,败者树。