取出整数的每一位数_如何在10亿个整数中找出前1000个最大的数?

本文介绍了如何在10亿个整数中快速找出前1000个最大数的方法。文章讨论了冒泡排序、快速排序、文件存储和MapReduce等传统算法的优缺点,并详细阐述了使用小顶堆作为最优解的实现过程,包括构建小顶堆的步骤和代码实现。这种方法在内存中维护一个长度为N的小顶堆,通过比较和堆调整,能够在较短时间内完成目标。
摘要由CSDN通过智能技术生成

8d037143cb18fb26cec8320adab7f7c1.png

作者:vincent-duan,专注 Java,沉迷开源,架构师社区合伙人!

面试题目:如何在10亿个整数中找出前1000个最大的数。

我们知道排序算法有很多:

  1.  冒泡算法:通过两层for循环,外层第一次循环找到数组中最大的元素放置在倒数第一个位置,第二次循环找到第二大的元素放置在倒数第二个位置。。。循环N次就可以找到TopN。
    缺点:冒泡排序内层循环需要大量交换元素。复杂度介于O(n)和O(n^2)之间。

  2. 快速排序:选一个基准元素,每次排序可以将这个基准元素搁置在正确的位置,左边都是比基准小的元素,右边都是比基准大的元素从而将数组分成左右两部分,分而治之。TopN问题也同样如此,选择一个基准元素并通过快速排序将基准元素搁置在正确的位置,如果左边的元素个数小于1000,那么继续从基准右边排序,如果左边元素个数大于1000,那么从基准左边排序,直到基准的位置正好在1000,结束。
    缺点:第一次排序复杂度是O(n),第二次排序复杂度是O(n/2),第三次排序复杂度是O(n/4)....

  3. 文件存储,分而治之:

    将比基准小的元素存储在txt1中,比基准大的文件存储在txt2中,然后通过类似方法二的形式,最后求出TopN。

    缺点:磁盘读取,写入次数过多。

  4. MapReduce:单机内存和性能确实受限,那么我们可以将10亿个分段存储在不同的机器上,每台机器计算各自的TopN,最后汇总。
    缺点:空间换时间。

最优解:小顶堆

    在内存中维护一个长度为N的数组,根据堆的性质,每一个节点都比他的左右子节点小,先取出前N个数并构建小顶堆,然后将所有数据与堆顶比较大小,如果比堆顶小就直接丢弃,如果比堆顶大则替换堆顶,并且重新构建这个堆。

    构建小顶堆的过程:先要找到最后一个非叶子节点,数组的长度为6,那么最后一个非叶子节点就是:长度/2-1,也就是6/2-1=2,然后下一步就是比较该节点值和它的左右节点值,如果该节点大于其左\右子树的值就交换(意思就是将最小的值放到该节点)。如果该节点不是叶子结点,则递归这一过程,直到这个节点变成叶子节点。

具体执行代码如下:

import java.util.Random;

/**
* @author vincent
* @time 2019-08-07 11:59
*/
public class TopN {
public static int N = 10; //Top10
public static int LEN = 100000000; //1亿个整数
public static int arrs[] = new int[LEN];
public static int result[] = new int[N]; //在内存维护一个长度为N的小顶堆
public static int len = result.length;
//堆中元素的有效元素 heapSize<=len
public static int heapSize = len;
public static void main(String[] args) {
//生成随机数组
for(int i = 0;i arrs[i] = new Random().nextInt(999999999);
}

//构建初始堆
for(int i = 0;i result[i] = arrs[i];
}
//构建小顶堆
long start =System.currentTimeMillis();
buildMinHeap();
for(int i = N;iif(arrs[i] > result[0]){
result[0] = arrs[i];
minHeap(0);
}
}
System.out.println(LEN+"个数,求Top"+N+",耗时"+(System.currentTimeMillis()-start)+"毫秒");
print();
}


/**
* 自底向上构建小堆
*/
public static void buildMinHeap(){
int size = len / 2 -1 ; //最后一个非叶子节点
for(int i = size;i>=0;i--){
minHeap(i);
}
}

/**
* i节点为根及子树是一个小堆
* @param i
*/
public static void minHeap(int i){
int l = left(i);
int r = right(i);
int index = i;
if(l index = l;
}
if(r index = r;
}
if(index != i){
int t = result[index];
result[index] = result[i];
result[i] = t;
//递归向下构建堆
minHeap(index);
}
}

/**
* 返回i节点的左孩子
* @param i
* @return
*/
public static int left(int i){
return 2*i;
}

/**
* 返回i节点的右孩子
* @param i
* @return
*/
public static int right(int i){
return 2*i+1;
}
/**
* 打印
*/
public static void print(){
for(int a: result){
System.out.print(a+",");
}
System.out.println();
}
}

长按订阅更多精彩▼

744f1b2c663586536d67ad3acbeea8a6.png

如有收获,点个在看,诚挚感谢412a41a38d9906db940c28aba4e00a20.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值