java 查找最小的k个元素_原创:从海量数据中查找出前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[] array = 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) {

array[index] = Integer.parseInt(text);

}

index ++;

} while (text != null && index <= k - 1);

MinHeap heap = new MinHeap(array);//初始化堆

for (int i : heap.heap) {

System.out.print(i + " ");

}

heap.BuildMinHeap();//构建小顶堆

System.out.println();

System.out.println("构建小顶堆之后:");

for (int i : heap.heap) {

System.out.print(i + " ");

}

System.out.println();

// 遍历文件中剩余的n(文件数据容量,假设为无限大)-k条数据,如果读到的数据比heap[0]大,就替换之,同时更新堆

while (text != null) {

text = reader.readLine();

if (text != null && !"".equals(text.trim())) {

if (Integer.parseInt(text) > heap.heap[0]) {

heap.heap[0] = Integer.parseInt(text);

heap.Minify(0);//调整小顶堆

}

}

}

//最后对堆进行排序(降序)

heap.HeapSort();

Long end = System.nanoTime();

long time = end - start;

System.out.println("用时:"+ time + "纳秒");

for (int i : heap.heap) {

System.out.println(i);

}

return heap.heap;

}

}

package findMinNumIncludedTopN;

/**

* 大顶堆

* @author TongXueQiang

* @date 2016/03/09

* @since JDK 1.7

*/

public class MaxHeap {

int[] heap;

int heapsize;

public MaxHeap(int[] array) {

this.heap = array;

this.heapsize = heap.length;

}

public void BuildMaxHeap() {

for (int i = heapsize / 2 - 1; i >= 0; i--) {

Maxify(i);// 依次向上将当前子树最大堆化

}

}

public void HeapSort() {

for (int i = 0; i < heap.length; i++) {

// 执行n次,将每个当前最大的值放到堆末尾

swap(heap,0,heapsize-1);

heapsize--;

Maxify(0);

}

}

public void Maxify(int i) {

int l = 2*i + 1;

int r = 2*i + 2;

int largest;

if (l < heapsize && heap[l] > heap[i])

largest = l;

else

largest = i;

if (r < heapsize && heap[r] > heap[largest])

largest = r;

if (largest == i || largest >= heapsize)// 如果largest等于i说明i是最大元素

// largest超出heap范围说明不存在比i节点大的子女

return;

swap(heap,i,largest);

Maxify(largest);

}

private void swap(int[] heap, int i, int largest) {

int tmp = heap[i];// 交换i与largest对应的元素位置,在largest位置递归调用maxify

heap[i] = heap[largest];

heap[largest] = tmp;

}

public void IncreaseValue(int i, int val) {

heap[i] = val;

if (i >= heapsize || i <= 0 || heap[i] >= val)

return;

int p = Parent(i);

if (heap[p] >= val)

return;

heap[i] = heap[p];

IncreaseValue(p, val);

}

private int Parent(int i) {

return (i - 1) / 2;

}

}

package findMinNumIncludedTopN;

/**

* 小顶堆

* @author TongXueQiang

* @date 2016/03/09

* @since JDK 1.7

*/

public class MinHeap {

int[] heap;

int heapsize;

public MinHeap(int[] array) {

this.heap = array;

this.heapsize = heap.length;

}

/**

* 构建小顶堆

*/

public void BuildMinHeap() {

for (int i = heapsize / 2 - 1; i >= 0; i--) {

Minify(i);// 依次向上将当前子树最大堆化

}

}

/**

* 堆排序

*/

public void HeapSort() {

for (int i = 0; i < heap.length; i++) {

// 执行n次,将每个当前最大的值放到堆末尾

swap(heap,0,heapsize-1);

heapsize--;

Minify(0);

}

}

/**

* 对非叶节点调整

* @param i

*/

public void Minify(int i) {

int l = 2*i + 1;

int r = 2*i + 2;

int min;

if (l < heapsize && heap[l] < heap[i])

min = l;

else

min = i;

if (r < heapsize && heap[r] < heap[min])

min = r;

if (min == i || min >= heapsize)// 如果largest等于i说明i是最大元素

// largest超出heap范围说明不存在比i节点大的子女

return;

swap(heap,i,min);

Minify(min);

}

private void swap(int[] heap, int i, int min) {

int tmp = heap[i];// 交换i与largest对应的元素位置,在largest位置递归调用maxify

heap[i] = heap[min];

heap[min] = tmp;

}

public void IncreaseValue(int i, int val) {

heap[i] = val;

if (i >= heapsize || i <= 0 || heap[i] >= val)

return;

int p = Parent(i);

if (heap[p] >= val)

return;

heap[i] = heap[p];

IncreaseValue(p, val);

}

private int Parent(int i) {

return (i - 1) / 2;

}

}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值