现在有这么一道题目:要求从多个的数据中查找出前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已经老化,双核,杂牌的。

原创:从海量数据中查找出前k个最小或最大值的算法(java)的更多相关文章

  1. 海量数据中找出前k大数(topk问题)

    海量数据中找出前k大数(topk问题) 前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小 ...

  2. 从海量数据中寻找出topK的最优算法代码

    package findMinNumIncludedTopN;/** * 小顶堆 * @author TongXueQiang * @date 2016/03/09 * @since JDK 1.8  ...

  3. java中从1000万个随机数中查找出相同的10万个随机数花的最少时间

    偶然在群里看到有人问到大数据查询,自己也就想了小艾改如何解决,从从1000万个随机数中查找出相同的10万个随机数花的最少时间, 谈到效率,自然是hashmap莫属. import java.util. ...

  4. LeetCode--034--在排序数组中查找元素的第一个和最后一个位置(java)

    给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置. 你的算法时间复杂度必须是 O(log n) 级别. 如果数组中不存在目标值,返回 [ ...

  5. 34、在排序数组中查找元素的第一个和最后一个位置 | 算法(leetode,附思维导图 + 全部解法)300题

    零 标题:算法(leetode,附思维导图 + 全部解法)300题之(34)在排序数组中查找元素的第一个和最后一个位置 一 题目描述 二 解法总览(思维导图) 三 全部解法 1 方案1 1)代码: / ...

  6. 面试突击 | Redis 如何从海量数据中查询出某一个 Key?附视频

    1 考察知识点 本题考察的知识点有以下几个: Keys 和 Scan 的区别 Keys 查询的缺点 Scan 如何使用? Scan 查询的特点 2 解答思路 Keys 查询存在的问题 Scan 的使用 ...

  7. Redis实战(20)Redis 如何从海量数据中查询出某一个 Key?

    序言 资料 https://www.cnblogs.com/vipstone/p/12373734.html

  8. 从数组中找出第K大的数

    利用改进的快排方法 public class QuickFindMaxKValue { public static void main(String[] args) { int[] a = {8, 3 ...

  9. 从海量文本中统计出前k个频率最高的词语

    现有如下题目:有一个海量文本,存储的是汉语词语,要求从中找出前K个出现频率最高的词语,写出最优算法,兼顾时间和空间复杂度. 思路分析:熟悉搜索引擎的程序员,应该不是难题.用传统的HashMap是无法解 ...

随机推荐

  1. 解决 WPF 嵌套的子窗口在改变窗口大小的时候闪烁的问题

    原文:解决 WPF 嵌套的子窗口在改变窗口大小的时候闪烁的问题 因为 Win32 的窗口句柄是可以跨进程传递的,所以可以用来实现跨进程 UI.不过,本文不会谈论跨进程 UI 的具体实现,只会提及其实现 ...

  2. 关于Shareppoint客户端对象模型和Shareppoint根据内部名称获取字段值的随笔

    实际上,每个SharePoint字段实际上有两个名称,一个是“标题”(Title,有时候也把它叫做“显示名称”),一个是“内部名称”(Internal Name).平时用户在列表视图界面上看到的,都是 ...

  3. ubuntu ufw 配置

    ubuntu ufw 配置 Ubuntu 18.04 LTS 系统中已经默认附带了 UFW 工具,如果您的系统中没有安装,可以在「终端」中执行如下命令进行安装: 1 sudo apt install ...

  4. Python小爬虫-读取豆瓣电影名称导出csv

    # -*- coding: utf-8 -*- __author__ = 'YongCong Wu' # @Time : 2019/6/20 10:27 # @Email : : 1922878025 ...

  5. UI5-技术篇-签字板

    签字板应用是通过创建自定义控件实现的,相关代码如下: 1.HTML <!DOCTYPE HTML> <html> <head> <meta http-equi ...

  6. Linux (x86) Exploit 开发系列教程之三(Off-By-One 漏洞 (基于栈))

    off by one(栈)? 将源字符串复制到目标缓冲区可能会导致off by one 1.源字符串长度等于目标缓冲区长度. 当源字符串长度等于目标缓冲区长度时,单个NULL字节将被复制到目标缓冲区上 ...

  7. go语言实现分布式锁

    本文:https://chai2010.cn/advanced-go-programming-book/ch6-cloud/ch6-02-lock.html 分布式锁 在单机程序并发或并行修改全局变量 ...

  8. 利用.bat脚本使得可运行jar开机自动运行

    1.利用Elipse到处可运行的jar包 2.写.bat脚本[点此下载],相应目录自己根据需要修改即可 3.将此脚本放在"启动"文件夹中

  9. 【idea】scala&sbt+idea+spark使用过程中问题汇总(不定期更新)

    本地模式问题系列: 问题一:会报如下很多NoClassDefFoundError的错误,原因缺少相关依赖包 Exception in thread "main" java.lang ...

  10. Deep learning_CNN_Review:A Survey of the Recent Architectures of Deep Convolutional Neural Networks——2019

    CNN综述文章 的翻译 [2019 CVPR] A Survey of the Recent Architectures of Deep Convolutional Neural Networks 翻 ...