从海量数据中查找出前k个最小或最大值的算法(java)

Posted 佟学强

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从海量数据中查找出前k个最小或最大值的算法(java)相关的知识,希望对你有一定的参考价值。

现在有这么一道题目:要求从多个的数据中查找出前K个最小或最大值

分析:有多种方案可以实现。一、最容易想到的是先对数据快速排序,然后输出前k个数字。

               二、先定义容量为k的数组,从源数据中取出前k个填充此数组,调整此数组的最大值maxValue到首位,然后对剩下的n-k个数据迭代,对于每个遍历到的数字x,如果x < maxValue,用x把maxValue替换掉,然后调整数组最大值的位置。

             三、基于二的思路,维护容量为k的堆,从源数据中取出前k个填充实例化堆,调整此堆中的最大值maxValue到堆顶,然后对剩下的n-k个数据迭代,对于每个遍历到的数字x,如果x < maxValue,用x把maxValue替换掉,然后调整堆最大值的位置。

             还有其他的方案,省略。

下面分别计算时间复杂度和空间复杂度。

                     时间复杂度                                    空间复杂度

方案一         O( n*lgn + k)          在栈中定义数组,几乎不占用堆内存

方案二         O(K + (n-k)*k)          在栈中定义数组,几乎不占用堆内存 

方案三         O(K + (n-k)*lgk)         O(k)

当n趋于无穷大的时候,很显然,方案三是最有选择,而且,当数据量非常的时候,方案一根本行不通,因为一个数组根本存不下海量数据,实际上,也几乎没有一个人这样写算法。快排的时间复杂度是n*lgn,如果把数据放入堆中,事实证明,在堆中对数据的操作,时间复杂度均为lgk,其中k为堆的容量。今天写了方案三的java代码,分享如下:

package findMinNumIncludedTopN;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

/**
* 从海量数据中查找出前k个最小值,精确时间复杂度为:K + (n - K) * lgk,空间复杂度为 O(k),目前为所有算法中最优算法
*
* @author TongXueQiang
* @date 2016/03/08
* @since JDK 1.7
*/
public class FindMinNumIncluedTopN {
/**
* 从海量数据中查找出前k个最小值
*
* @param k
* @return
* @throws IOException
*/
public int[] findMinNumIncluedTopN(int k) throws IOException {
Long start = System.nanoTime();

int[] heap = new int[k];
int index = 0;
// 从文件导入海量数据
BufferedReader reader = new BufferedReader(new FileReader(new File("F:/number.txt")));
String text = null;
// 先读出前n条数据,构建堆
do {
text = reader.readLine();
if (text != null) {
heap[index] = Integer.parseInt(text);
}
index ++;
} while (text != null && index <= k - 1);

buildHeap(heap);// 建堆,并调整最大值的位置为首位

// 遍历文件中剩余的n(文件数据容量,假设为无限大)-k条数据,如果读到的数据比heap[0]小,就替换之,同时更新堆
while (text != null) {
text = reader.readLine();
if (text != null && !"".equals(text.trim())) {
if (Integer.parseInt(text) < heap[0]) {
heap[0] = Integer.parseInt(text);
maxHeap(heap);
}
}
}
Long end = System.nanoTime();
long time = end - start;
System.out.print("用时:"+ time + "纳秒");
return heap;
}

/**
* 构建堆
*
* @param heap
*/
public void buildHeap(int[] heap) {
maxHeap(heap);
}

/**
* 更新堆数据,把最大值调整为首位,比较交换算法,so easy!
*
* @param heap
*/
public void maxHeap(int[] heap) {
int max = heap[0];
int largeIndex = 0;
//找出最大索引位置,从第二个位置开始
for (int i = 1; i < heap.length; i++) {
if (heap[i] > max) {
max = heap[i];
largeIndex = i;
}
}
//交换
swap(heap, largeIndex);
}

/**
* 数据交换
*
* @param heap
* @param largeIndex
*/
private void swap(int[] heap, int largeIndex) {
int temp;
temp = heap[0];
heap[0] = heap[largeIndex];
heap[largeIndex] = temp;
}
}

测试类:

public class Test {

public static void main(String[] args) throws Exception {
FindMinNumIncluedTopN fmnt4 = new FindMinNumIncluedTopN();
int heap[] = fmnt4.findMinNumIncluedTopN(4);
}
}

从一个14.2M的文件中读取数据(大约有130多万条数据),找出前4个最小值,耗时平均为0.6秒,效果很好,而且本人的电脑硬件配置相当烂,CPU已经老化,双核,杂牌的。

以上是关于从海量数据中查找出前k个最小或最大值的算法(java)的主要内容,如果未能解决你的问题,请参考以下文章

海量数据处理 大量数据中找出最大的前10个数 (Top K 问题)

想在含有n个元素的序列中得到最小的前k个元素,最好采用啥排序算法

选择树判定树和查找树

海量数据问题

python使用heapq快速查找最大或最小的 N 个元素

海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)