点击上方蓝色“终端研发部”,选择“设为星标”

学最好的别人,做最好的我们

作者:foreach_break

来源:https://blog.csdn.net/gsky1986/article/details/46499529

最近,面试头条,面试官一上来,就问了我这么一个问题,我一脸懵逼,决定记录一下。

# 问题

给你1个文件bigdata,大小4663M,5亿个数,文件中的数据随机,如下一行一个整数:

6196302
3557681
6121580
2039345
2095006
1746773
7934312
2016371
7123302
8790171
2966901
...
7005375

现在要对这个文件进行排序,怎么搞?

# 内部排序

先尝试内排,选2种排序方式。

3路快排:

private final int cutoff = 8;public <T> void perform(Comparable<T>[] a) {perform(a,0,a.length - 1);}private <T> int median3(Comparable<T>[] a,int x,int y,int z) {
if(lessThan(a[x],a[y])) {
if(lessThan(a[y],a[z])) {
return y;}
else if(lessThan(a[x],a[z])) {
return z;}else {
return x;}}else {
if(lessThan(a[z],a[y])){
return y;}else if(lessThan(a[z],a[x])) {
return z;}else {
return x;}}}private <T> void perform(Comparable<T>[] a,int low,int high) {
int n = high - low + 1;
//当序列非常小,用插入排序
if(n <= cutoff) {InsertionSort insertionSort = SortFactory.createInsertionSort();insertionSort.perform(a,low,high);
//当序列中小时,使用median3}else if(n <= 100) {
int m = median3(a,low,low + (n >>> 1),high);exchange(a,m,low);
//当序列比较大时,使用ninther}else {
int gap = n >>> 3;
int m = low + (n >>> 1);
int m1 = median3(a,low,low + gap,low + (gap << 1));
int m2 = median3(a,m - gap,m,m + gap);
int m3 = median3(a,high - (gap << 1),high - gap,high);
int ninther = median3(a,m1,m2,m3);exchange(a,ninther,low);}if(high <= low)
return;
//lessThan
int lt = low;
//greaterThan
int gt = high;
//中心点Comparable<T> pivot =  a[low];
int i = low + 1;/** 不变式:*   a[low..lt-1] 小于pivot -> 前部(first)*   a[lt..i-1] 等于 pivot -> 中部(middle)*   a[gt+1..n-1] 大于 pivot -> 后部(final)**   a[i..gt] 待考察区域*/while (i <= gt) {
if(lessThan(a[i],pivot)) {
//i-> ,lt ->exchange(a,lt++,i++);}else if(lessThan(pivot,a[i])) {exchange(a,i,gt--);}else{i++;}}// a[low..lt-1] < v = a[lt..gt] < a[gt+1..high].perform(a,low,lt - 1);perform(a,gt + 1,high);}

归并排序:

/**     * 小于等于这个值的时候,交给插入排序     */private final int cutoff = 8;
/**     * 对给定的元素序列进行排序     *     * @param a 给定元素序列     */@Overridepublic <T> void perform(Comparable<T>[] a) {        Comparable<T>[] b = a.clone();        perform(b, a, 0, a.length - 1);    }
private <T> void perform(Comparable<T>[] src,Comparable<T>[] dest,int low,int high) {if(low >= high)return;
//小于等于cutoff的时候,交给插入排序if(high - low <= cutoff) {            SortFactory.createInsertionSort().perform(dest,low,high);return;        }
int mid = low + ((high - low) >>> 1);        perform(dest,src,low,mid);        perform(dest,src,mid + 1,high);
//考虑局部有序 src[mid] <= src[mid+1]if(lessThanOrEqual(src[mid],src[mid+1])) {            System.arraycopy(src,low,dest,low,high - low + 1);        }
//src[low .. mid] + src[mid+1 .. high] -> dest[low .. high]        merge(src,dest,low,mid,high);    }
private <T> void merge(Comparable<T>[] src,Comparable<T>[] dest,int low,int mid,int high) {
for(int i = low,v = low,w = mid + 1; i <= high; i++) {if(w > high || v <= mid && lessThanOrEqual(src[v],src[w])) {                dest[i] = src[v++];            }else {                dest[i] = src[w++];            }        }    }

数据太多,递归太深 ->栈溢出?加大Xss?

数据太多,数组太长 -> OOM?加大Xmx?

耐心不足,没跑出来.而且要将这么大的文件读入内存,在堆中维护这么大个数据量,还有内排中不断的拷贝,对栈和堆都是很大的压力,不具备通用性。

# sort命令来跑

跑了多久呢?24分钟。

为什么这么慢?

粗略的看下我们的资源:

内存 jvm-heap/stack,native-heap/stack,page-cache,block-buffer 外存 swap + 磁盘 数据量很大,函数调用很多,系统调用很多,内核/用户缓冲区拷贝很多,脏页回写很多,io-wait很高,io很繁忙,堆栈数据不断交换至swap,线程切换很多,每个环节的锁也很多。

总之,内存吃紧,问磁盘要空间,脏数据持久化过多导致cache频繁失效,引发大量回写,回写线程高,导致cpu大量时间用于上下文切换,一切,都很糟糕,所以24分钟不细看了,无法忍受。

位图法

private BitSet bits;
public void perform(            String largeFileName,int total,            String destLargeFileName,            Castor<Integer> castor,int readerBufferSize,int writerBufferSize,            boolean asc) throws IOException {System.out.println("BitmapSort Started.");long start = System.currentTimeMillis();        bits = new BitSet(total);        InputPart<Integer> largeIn = PartFactory.createCharBufferedInputPart(largeFileName, readerBufferSize);        OutputPart<Integer> largeOut = PartFactory.createCharBufferedOutputPart(destLargeFileName, writerBufferSize);        largeOut.delete();Integer data;int off = 0;try {while (true) {                data = largeIn.read();if (data == null)break;int v = data;set(v);                off++;            }            largeIn.close();int size = bits.size();            System.out.println(String.format("lines : %d ,bits : %d", off, size));
if(asc) {for (int i = 0; i < size; i++) {if (get(i)) {                        largeOut.write(i);                    }                }            }else {for (int i = size - 1; i >= 0; i--) {if (get(i)) {                        largeOut.write(i);                    }                }            }largeOut.close();long stop = System.currentTimeMillis();long elapsed = stop - start;            System.out.println(String.format("BitmapSort Completed.elapsed : %dms",elapsed));        }finally {            largeIn.close();            largeOut.close();        }    }
private void set(int i) {        bits.set(i);    }
private boolean get(int v) {return bits.get(v);    }

nice!跑了190秒,3分来钟. 以核心内存4663M/32大小的空间跑出这么个结果,而且大量时间在用于I/O,不错。

问题是,如果这个时候突然内存条坏了1、2根,或者只有极少的内存空间怎么搞?

# 外部排序

该外部排序上场了,外部排序干嘛的?

内存极少的情况下,利用分治策略,利用外存保存中间结果,再用多路归并来排序;

map-reduce的嫡系。

1、分

内存中维护一个极小的核心缓冲区memBuffer,将大文件bigdata按行读入,搜集到memBuffer满或者大文件读完时,对memBuffer中的数据调用内排进行排序,排序后将有序结果写入磁盘文件bigdata.xxx.part.sorted. 循环利用memBuffer直到大文件处理完毕,得到n个有序的磁盘文件:

2、合

现在有了n个有序的小文件,怎么合并成1个有序的大文件?把所有小文件读入内存,然后内排?(⊙o⊙)… no!

利用如下原理进行归并排序:

我们举个简单的例子:

文件1:3,6,9
文件2:2,4,8
文件3:1,5,7第一回合:
文件1的最小值:3 , 排在文件1的第1行
文件2的最小值:2,排在文件2的第1行
文件3的最小值:1,排在文件3的第1行
那么,这3个文件中的最小值是:min(1,2,3) = 1
也就是说,最终大文件的当前最小值,是文件1、2、3的当前最小值的最小值,绕么?
上面拿出了最小值1,写入大文件.第二回合:
文件1的最小值:3 , 排在文件1的第1行
文件2的最小值:2,排在文件2的第1行
文件3的最小值:5,排在文件3的第2行
那么,这3个文件中的最小值是:min(5,2,3) = 2
将2写入大文件.也就是说,最小值属于哪个文件,那么就从哪个文件当中取下一行数据.(因为小文件内部有序,下一行数据代表了它当前的最小值)

最终的时间,跑了771秒,13分钟左右。

less bigdata.sorted.text
...
9999966
9999967
9999968
9999969
9999970
9999971
9999972
9999973
9999974
9999975
9999976
9999977
9999978
...
BAT等大厂Java面试经验总结 想获取 Java大厂面试题学习资料扫下方二维码回复「BAT」就好了回复 【加群】获取github掘金交流群回复 【电子书】获取2020电子书教程回复 【C】获取全套C语言学习知识手册回复 【Java】获取java相关的视频教程和资料回复 【爬虫】获取SpringCloud相关多的学习资料回复 【Python】即可获得Python基础到进阶的学习教程回复 【idea破解】即可获得intellij idea相关的破解教程回复 【BAT】即可获得intellij idea相关的破解教程关注我gitHub掘金,每天发掘一篇好项目,学习技术不迷路!

回复 【idea激活】即可获得idea的激活方式

回复 【Java】获取java相关的视频教程和资料

回复 【SpringCloud】获取SpringCloud相关多的学习资料

回复 【python】获取全套0基础Python知识手册

回复 【2020】获取2020java相关面试题教程

回复 【加群】即可加入终端研发部相关的技术交流群

为什么HTTPS是安全的

因为BitMap,白白搭进去8台服务器...

《某厂内部SQL大全 》.PDF

字节跳动一面:i++ 是线程安全的吗?

大家好,欢迎加我微信,很高兴认识你!

在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!

相信自己,没有做不到的,只有想不到的

在这里获得的不仅仅是技术!

喜欢就给个“在看

头条面试官:5 亿整数的大文件,如何排序 ?相关推荐

  1. 多路归并排序_字节跳动面试:5 亿整数的大文件,如何排序 ?

    最近,面试头条,面试官一上来,就问了我这么一个问题,我一脸懵逼,决定记录一下. 问题 给你1个文件bigdata,大小4663M,5亿个数,文件中的数据随机,如下一行一个整数: 6196302 355 ...

  2. 面试:5 亿整数的大文件,来排个序?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | xupengzhang 来源 | import ...

  3. 5亿整数的大文件,怎么排序 ?面试被问傻!

    来源 | 程序员追风 编辑 | Carol 出品| CSDN云计算(ID:CSDNcloud) 最近一家公司,面试官一上来,就问了我这么一个问题,我一脸懵逼,决定记录一下. 问题 给你1个文件bigd ...

  4. 5亿整数的大文件,怎么排?

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 问题 给你1个文件bigdata,大小4663M,5亿个数,文件中的数据随机,如下一行一个整数: ...

  5. 面试题热个身:5 亿整数的大文件,来排个序?

    一.问题 给你1个文件bigdata,大小4663M,5亿个数,文件中的数据随机,如下一行一个整数: 现在要对这个文件进行排序,怎么搞? 二.内部排序 先尝试内排,选2种排序方式: privatefi ...

  6. 靠一个HashMap的讲解打动了头条面试官,我的秘诀是

    最近收集了一份github标星81.6k的Java面试突击手册,文末查看 关注 转发+转发+转发 私信回复关键词 [学习]即可获取~ 预备知识 位运算知识 位运算操作是由处理器支持的底层操作,底层硬件 ...

  7. 面试官:手撕十大排序算法,你会几种?

    作者: C you again,从事软件开发 努力在IT搬砖路上的技术小白 公众号: [C you again],分享计算机类毕业设计源码.IT技术文章.游戏源码.网页模板.程序人生等等.公众号回复 ...

  8. 本题要求将给定的n个整数从大到小排序后输出。

    本题要求将给定的n个整数从大到小排序后输出. 输入格式: 输入第一行给出一个不超过10的正整数n.第二行给出n个整数,其间以空格分隔. 输出格式: 在一行中输出从大到小有序的数列,相邻数字间有一个空格 ...

  9. c语言冒泡法对10个整数由大到小排序,用冒泡法对10个整数排序

    公告: 为响应国家净网行动,部分内容已经删除,感谢读者理解. 话题:用冒泡法对10个整数排序.10个整数用scanf函数输入回答:举了例:一个数组:3,2,5,1,4从小到大排序从左侧开始,逐对比较3 ...

最新文章

  1. Android WebView 支持H5的定位Js
  2. Draw Circle 沿着圆运动~~
  3. Go 定时器内部实现原理剖析
  4. c调用按钮点击事件_Unity3d---对UI事件接口的一些测试和机制(坑)的总结
  5. 小明滚出---响应对象HttpServletResponse和请求对象HttpServletRequest实例
  6. Spring AMQP + Rabbit 配置多数据源消息队列
  7. 随手笔记:我的架构师之路--沈剑 到家集团技术委员会主席快狗打车CTO
  8. matlab画迟滞迥线,[画图的问题]怎么画类似于磁滞回线的图像?一个x值对应两个y值的...
  9. Android-基本控件和详解四种布局方式
  10. robot脚本编写规范
  11. Java并发:整理自《Java并发编程实战》和《Java并发编程的艺术》
  12. 手机内置摄像头接线图解_坚果R2 手机官方壁纸
  13. revit 对计算机最低配置,Revit2016官方推荐电脑配置要求
  14. VGA带音频转HDMI转换芯片|VGA转HDMI 转换器方案|VGA转HDMI1.4转换器芯片介绍
  15. 从零开始之驱动发开、linux驱动(二十五、framebuffer 子系统框架)
  16. 深入探索透视纹理映射(下)
  17. 兆鹏带你读watir——【第五篇】watir的js应用(技巧篇)
  18. Java中IO流(3).
  19. vue样式中背景图片路径_vue-cli3.0全局less样式中该如何正确设置背景图片的路径?...
  20. SpringBoot整合微信支付开发在线教育视频网站(完整版)

热门文章

  1. ①GD32Keil编译环境搭建及编译Demo
  2. CreatorPrimer | 加载预制件
  3. Wi n E x e c和O p e n F i l e等,只是为了实现与1 6位Wi n d o w s程 序的向后兼容而存在
  4. NKOJ 2139 鸡蛋染色
  5. 小白也能看懂的网络基础 | 44 张图搞定什么是连接设备?
  6. 陆奇看好的创业项目,16/22 个都由 AI 驱动
  7. RZ/G2L核心板CPU温升测试
  8. checklistbox(checklistbox)
  9. 漏洞检测与防御:Redis未授权访问漏洞复现
  10. ubuntu换镜像源与进程