求100亿个数的中位数

1、题目描述

    给定100亿个无符号的乱序的整数序列,如何求出这100亿个数的中位数(中位数指的是排序后最中间那个数)。
2、解题思路一

    一个无符号整数的大小为4B,则100亿个数的大小为40GB,如果内存够大的话可以对这100亿个数加载到内存中,然后使用堆排序或者快速排序进行排序,取出中位数即可。使用快排时,每次划分之后只需要比较枢纽值的索引和50亿比较,然后只对两个划分中的一个进行递归排序即可,而不用整体进行排序。使用堆排时,建立堆之后,需要进行50亿次的调整即可。
3、解题思路二

    中位数问题可以看做一个统计问题,而不是排序问题,无符号整数大小为4B,则能表示的数的范围为为0 ~ 2^32 - 1(40亿),则可以用一个2^32(4GB)大小的数组(也叫做桶)来保存100亿个数中每个无符号数出现的次数。遍历这100亿个数,当元素值等于桶元素索引时,桶元素的值加1。当统计完100亿个数以后,则从索引为0的值开始累加桶的元素值,当累加值等于50亿时,这个值对应的索引为中位数。时间复杂度为O(n)。
4、解题思路三

    如果内存的大小小于4GB时(假设内存为512M),这时怎么办呢?解决方案为使用区间的桶排序,解题思路如下:

(1)如果只有512M的内存,则512M内存可以装2^(9 + 10 + 10) = 536,870,912个无符整数,约为5亿左右,接着把无符号整数的范围0~40亿划分为每10个数一个区间,也就有4亿个区间,划分后第一个区间0~9,第二个区间10~19,......在内存中使用4亿个数来保存100亿个数中落在每个区间的整数个数。此时内存中还可以存放1亿个数,分100次把100亿个数加载到内存,每次加载1亿个,统计落到每个区间的整数个数。

(2)第一步完成统计之后,可以知道落到4亿个区间中每个区间的整数个数,然后从最小区间向最大区间开始累加,当累加的数达到50亿时,记住这个区间起点位置和终点位置(终点 - 起点 + 1 = 10)和没有加这个区间统计个数时的整数个数。

(3)知道第50亿个数所落在的区间起点和终点位置后,接着对区间的每个数设置一个桶(这里总共为10个桶),用来统计每个数的元素个数。接着对100亿个数分20批次进行遍历,每次加载到内存5亿个数。

(4)统计完成之后就可以知道落到区间内的每个元素个数,接着对区间的统计个数进行累加,当这个累加值加上(2)中保存的没有加上该区间的整数个数等于50亿时,该数对应的索引就位中位数。

总结:整个过程需要遍历100亿个数两次,第一次确定第50亿个数所落在的索引区间,第二次确定第50亿个数所落在的索引。区间大小是一个可以优化的值,优化之后可以使得I/O的次数最少。

你可能感兴趣的:(算法题)