如何给大数据量的磁盘文件排序

本文是学习 july博客中的如何给 10^7个数据量的磁盘文件排序,这一文章的学习文章。

July博客

问题:

输入:给定一个文件,里面有 最多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中的实现

C++中rand  srand的用法

rand()在每次运行的时候都是和上一次相同,srand就是用来更改rand的随机数种子。

JAVA获取随机数

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),删除。

本章学习,毕,留下任务:多路归并理论,败者树。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值