zoukankan      html  css  js  c++  java
  • 原创:从海量数据中查找出前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已经老化,双核,杂牌的。

  • 相关阅读:
    Thinkph5——模型软删除
    ThinkPHP5——URL参数带中文,报"模块不存在"
    ThinkPHP5多模板配置二级域名
    使用Composer安装阿里云短信失败
    GIT快速入门
    Python全栈之路系列----之-----面向对象4(接口与抽象,多继承与多态)
    Python全栈之路系列----之-----面向对象3(继承与派生)
    Python全栈之路系列----之-----面向对象1(初识)
    Python全栈之路系列----之-----异常处理
    Python全栈之路系列----之-----内置函数和匿名函数lamdba
  • 原文地址:https://www.cnblogs.com/txq157/p/5255103.html
Copyright © 2011-2022 走看看