堆排序应用之topK问题

题目:求海量数据(正整数)按逆序排列的前k个数(topK),因为数据量太大,不能全部存储在内存中,只能一个一个地从磁盘或者网络上读取数据,请设计一个高效的算法来解决这个问题。 第一行用户输入K,代表要求得topK  随后的N(不限制)行,每一行是一个整数代表用户输入的数据 直到用户输入-1代表输入终止 请输出topK,空格分割。

思路:先开辟一个K大小的数组arr,然后读取K个数据存储到数组arr,读到K+1的时候,如果arr[K+1]小于arr中最小的值,那么就丢掉不管,如果arr[K+1]大于arr中最小的值,那么就把arr[K+1]和数组中最小的值进行交换,然后再读取K+2个数。这样就能解决这个问题。但是这个算法复杂度为K+(N-K)*K,K可以忽略不计,所以时间复杂度为O(KN)。那这个代码很容易就写出来。假如题目要求用到NlgK的时间复杂度,那么这里就需要使用堆这种数据结构来解决问题,而且还是小顶堆。具体思想还是和数组一样。

代码:

 1 import java.util.Arrays;
 2 import java.util.Scanner;
 3
 4 public class TopK {
 5
 6     static int[] heap;
 7     static int index = 0;
 8     static int k;
 9
10     public static void main(String[] args) {
11         Scanner scanner = new Scanner(System.in);
12         k = scanner.nextInt();
13         heap = new int[k];
14         int x = scanner.nextInt();
15         while(x!=-1){
16             deal(x);  // 处理x
17             x = scanner.nextInt();
18         }
19         System.out.println(Arrays.toString(heap));
20     }
21
22     /**
23      * 如果数据量小于等于k,直接加入堆中
24      * 等于k的时候,进行堆化
25      * @param x
26      */
27     private static void deal(int x) {
28         if (index<k) {
29             heap[index++] = x;
30             if (index==k) {
31                 // 堆化
32                 makeMinHeap(heap);
33                 System.out.println(Arrays.toString(heap));
34             }
35         }else if (heap[0]<x) {
36             heap[0] = x;
37             MinHeapFixDown(heap, 0, k);
38             System.out.println(Arrays.toString(heap));
39         }else {
40             System.out.println(Arrays.toString(heap));
41         }
42     }
43     static void makeMinHeap(int[] A){
44         int n = A.length;
45         for(int i = n/2-1;i>=0;i--){
46             MinHeapFixDown(A,i,n);
47         }
48     }
49
50     private static void MinHeapFixDown(int[] A, int i, int n) {
51         //  找到左右孩子
52         int left = 2 * i + 1;
53         int right = 2 * i + 2 ;
54         // 左孩子已经越界,i就是叶子节点
55         if (left>=n) {
56             return ;
57         }
58         // min 指向了左右孩子中较小的那个
59         int min = left;
60         if (right>=n) {
61             min = left;
62         }else {
63             if (A[right]<A[left]) {
64                 min = right;
65             }
66         }
67         // 如果A[i]比两个孩子都要小,不用调整
68         if (A[i]<=A[min]) {
69             return ;
70         }
71         // 否则,找到两个孩子中较小的,和i交换
72         int temp = A[i];
73         A[i] = A[min];
74         A[min] = temp;
75         // 小孩子那个位置的值发生了变化,i变更为小孩子那个位置,递归调整
76         MinHeapFixDown(A, min, n);
77     }
78
79 }

结果:

  

总结:partition和堆都能解决顺序统计量问题,堆更适用于海量数据流,适用于不能全部存储在内存中的数据,相当于实时数据流,而partition适用于能够一次加载到内存的数组。

原文地址:https://www.cnblogs.com/xiaoyh/p/10290040.html

时间: 2024-10-25 12:44:41

堆排序应用之topK问题的相关文章

Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等

本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排序.快速排序(重点).堆排序.归并排序等等.看下图: 给定数组:int data[] = {9,2,7,19,100,97,63,208,55,78} 一.直接插入排序(内部排序.O(n2).稳定) 原理:从待排序的数中选出一个来,插入到前面的合适位置. [java] view plain copy

常见的几种排序算法-插入、选择、冒泡、快排、堆排等

排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排序.快速排序(重点).堆排序.归并排序等等.看下图: 给定数组:int data[] = {9,2,7,19,100,97,63,208,55,78} 一.直接插入排序(内部排序.O(n2).稳定) 原理:从待排序的数中选出一个来,插入到前面的合适位置. package com.xtfggef.algo.sort; public

minheap+hashmap组合解决动态topK问题(附堆排序完整实现)

TopK的解决方法一般有两种:堆排序和partition.前者用优先队列实现,时间复杂度为O(NlogK)(N为元素总数量),后者可以直接调用C++ STL中的nth_element函数,时间复杂度O(N).如果想获取动态更新数据的topK就不那么容易了,比如实时更新最常访问的top10的网址,显然除了维护一个size为10的最小堆以外还需要一个哈希表实时记录每一个网址的访问次数,并决定是否动态加入到最大堆中,同时可能删除堆中的元素.那么如何获得该网址在堆中的位置呢?需要另一个hashmap记录

关于堆排序和topK算法的PHP实现

问题描述 topK算法,简而言之,就是求n个数据里的前m大个数据,一般而言,m<<n,也就是说,n可能有几千万,而m只是10或者20这样的两位数. 思路 最简单的思路,当然是使用要先对这n个数据进行排序,因为只有排序以后,才能按照顺序来找出排在前面的,或者排在后面的数据. 假如说我们用快拍,那么时间复杂度是O(nlogn),但是仔细看题目,会发现实际上不要要将所有的数据就进行排序,因为我们找的是前m个数据,所以对所有数据排序实际上有些浪费了.所以可以想到,只维护一个大小为m的数组,然后扫一遍原

堆排序TopK

package test; import java.util.Random; public class TSort { public static void main(String[] args) { TSort tsort = new TSort(); tsort.test(); } public void test() { TopHundredHeap.test(null); } public void testt(String a, double[] d, String[] t, Stri

算法 排序NB二人组 堆排序 归并排序

参考博客:基于python的七种经典排序算法     常用排序算法总结(一) 序前传 - 树与二叉树 树是一种很常见的非线性的数据结构,称为树形结构,简称树.所谓数据结构就是一组数据的集合连同它们的储存关系和对它们的操作方法.树形结构就像自然界的一颗树的构造一样,有一个根和若干个树枝和树叶.根或主干是第一层的,从主干长出的分枝是第二层的,一层一层直到最后,末端的没有分支的结点叫做叶子,所以树形结构是一个层次结构.在<数据结构>中,则用人类的血统关系来命名,一个结点的分枝叫做该结点的"

排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结束.时间复杂度:O(n^2) 选择排序:每次在无序队列中"选择"出最大值,放到有序队列的最后,并从无序队列中去除该值(具体实现略有区别).时间复杂度:O(n^2) 直接插入排序:始终定义第一个元素为有序的,将元素逐个插入到有序排列之中,其特点是要不断的 移动数据,空出一个适当的位置,把待插

简单的topK问题

/************************************************************************/ /* 求一组数据中的top(K)问题,这是一个经典的top(K)问题. 分析: 方法一:如果数据量不大,那么最常用的方法就是排序从大大小,然后找出前k个数据. 比较高效率的排序算法,如快排,堆排序等,总体时间复杂度为 O(N*log2(N))+O(K)=O(N*log2(N)) 或是直接用部分排序算法,如选择排序,直接找出前K个元素,时间复杂度为O

Java 小根堆排序

Counter类:计数器 IntPk中包含主键 public class Counter extends IntPK{     private int count;     public int getCount() {         return count;     }     public void setCount(int count) {         this.count = count;     } } MinHeap类:最小堆排序类 package com.ryx.incan