在处理大量数据的时候,有时候往往需要找出Top前几的数据,这时候如果直接对数据进行排序,在处理海量数据的时候往往就是不可行的了,而且在排序最好的时间复杂度为nlogn,当n远大于需要获取到的数据的时候,时间复杂度就显得过高。

使用最小堆或者最大堆可以很好地解决Top大问题或者Top小问题。

  • Top大问题解决思路:使用一个固定大小的最小堆,当堆满后,每次添加数据的时候与堆顶元素比较,若小于堆顶元素,则舍弃,若大于堆顶元素,则删除堆顶元素,添加新增元素,对堆进行重新排序。
  • Top小问题解决思路:使用一个固定大小的最大堆,当堆满后,每次添加数据到时候与堆顶元素进行比较,若大于堆顶元素,则舍弃,若小于堆顶元素,则删除堆顶元素,添加新增元素,对堆进行重新排序。

对于n个数,取Top m个数,时间复杂度为O(nlogm),这样在n较大情况下,是优于nlogn的时间复杂度的。

比如10000个数据,取前100大的数,那么时间复杂度就是O(10000log100)。

因为在插入数据的时候需要遍历元素时间复杂度达到了O(10000),然后每次插入过程中进行调整的复杂度为O(log100),所以总体时间复杂度为O(10000log100)。

使用Java类库集合实现

Java集合中的PriorityQueue就可以实现最大堆或者最小堆,从名字可以知道该集合是优先队列,数据结构中的优先队列就是使用堆来实现的。

// 底层通过一个Object类型数据保存元素
transient Object[] queue; // 通过Comparator制定比较方法
private final Comparator<? super E> comparator; // 其中一个构造函数
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}

下面就使用PriorityQueue来实现最小堆和最大堆。

  • 在构造PriorityQueue的时候需要传入一个size和一个比较函数,制定堆中元素比较规则。
  • 重写compare(o1, o2)方法,最小堆使用o1 - o2,最大堆使用o2 - o1。
public class TopK<E extends Comparable> {
private PriorityQueue<E> queue;
private int maxSize; //堆的最大容量 public TopK(int maxSize) {
if (maxSize <= 0) {
throw new IllegalStateException();
}
this.maxSize = maxSize;
this.queue = new PriorityQueue<>(maxSize, new Comparator<E>() {
@Override
public int compare(E o1, E o2) {
// 最大堆用o2 - o1,最小堆用o1 - o2
return (o1.compareTo(o2));
}
});
} public void add(E e) {
if (queue.size() < maxSize) {
queue.add(e);
} else {
E peek = queue.peek();
if (e.compareTo(peek) > 0) {
queue.poll();
queue.add(e);
}
}
} public List<E> sortedList() {
List<E> list = new ArrayList<>(queue);
Collections.sort(list);
return list;
} public static void main(String[] args) {
int[] array = {4, 5, 1, 6, 2, 7, 3, 8};
TopK pq = new TopK(4);
for (int n : array) {
pq.add(n);
}
System.out.println(pq.sortedList());
}
}

运行结果:

使用Java实现

通过上述讲述,基本了解最大堆和最小堆情况以及它们与TopK问题的关系,上面是使用集合实现,下面使用Java来实现最小堆,并解决TopK大问题。

  • 限定数据大小。
  • 若堆满,则插入过程中与堆顶元素比较,并做相应操作。
  • 每次删除堆顶元素后堆做一次调整,保证最小堆特性。
public class TopK {
int[] items;
int currentSize = 0; // 初始化为size + 1,从下标1开始保存元素。
public TopK(int size) {
items = new int[size + 1];
} // 插入元素
public void insert(int x) {
if (currentSize == items.length - 1) {
if (compare(x, items[1]) < 0) {
return;
} else if (compare(x, items[1]) > 0) {
deleteMin();
}
} int hole = ++currentSize;
for (items[0] = x; compare(x, items[hole / 2]) < 0; hole /= 2) {
items[hole] = items[hole / 2];
}
items[hole] = x;
} // 删除最小堆中最小元素
public int deleteMin() {
int min = items[1];
items[1] = items[currentSize--];
percolateDown(1);
return min;
} // 下滤
public void percolateDown(int hole) {
int child;
int temp = items[1]; for (; hole * 2 <= currentSize; hole = child) {
child = 2 * hole;
if (child != currentSize && compare(items[child + 1], items[child]) == -1) {
child++;
}
if (compare(items[child], temp) < 0) {
items[hole] = items[child];
} else {
break;
}
}
items[hole] = temp;
} // 制定比较规则
public static int compare(int a, int b) {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
}
return 0;
} public static void main(String[] args) {
TopK topK = new TopK(10);
for (int i = 1; i <= 100; i++) {
topK.insert(i);
}
for (int j = 1; j <= topK.currentSize; j++) {
System.out.print(topK.items[j] + " ");
}
System.out.println();
}
}

运行结果:

Java解决TopK问题(使用集合和直接实现)的更多相关文章

  1. 如何解决TOP-K问题

    前言:最近在开发一个功能:动态展示的订单数量排名前10的城市,这是一个典型的Top-k问题,其中k=10,也就是说找到一个集合中的前10名.实际生活中Top-K的问题非常广泛,比如:微博热搜的前100 ...

  2. Atitit.excel导出 功能解决方案 php java C#.net版总集合.doc

    Atitit.excel导出 功能解决方案 php java C#.net版总集合.docx 1.1. Excel的保存格式office2003 office2007/2010格式1 1.2. 类库选 ...

  3. Java入门——(6)集合

       关键词:Collection接口.Map接口.Iterator接口.泛型.Collections工具类.Arrays工具类   一.集合概述      当数据多了需要存储,需要容器,而数据的个数 ...

  4. java内部类、接口、集合框架、泛型、工具类、实现类

    .t1 { background-color: #ff8080; width: 1100px; height: 40px } 一.内部类 1.成员内部类. (1)成员内部类的实例化: 外部类名.内部类 ...

  5. JAVA基础第五章-集合框架Map篇

    业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地! 我将会持续更新java基础知识,欢迎关注. 往期章节: JAVA基础第一 ...

  6. Java中的数组与集合

    此文转载自:http://student-lp.iteye.com/blog/2082362 在java编程的过程中,我们不能确定某一类型的对象到底会需要多少,为了解决这个问题,java提供了容纳对象 ...

  7. 复习java基础第四天(集合:List、Map、Collections、Enumeration)

    一.List: List 代表一个元素有序.且可重复的集合,集合中的每个元素都有其对应的顺序索引 List 允许使用重复元素,可以通过索引来访问指定位置的集合元素. List 默认按元素的添加顺序设置 ...

  8. 基于PriorityQueue(优先队列)解决TOP-K问题

    TOP-K问题是面试高频题目,即在海量数据中找出最大(或最小的前k个数据),隐含条件就是内存不够容纳所有数据,所以把数据一次性读入内存,排序,再取前k条结果是不现实的. 下面我们用简单的Java8代码 ...

  9. Java并发编程之set集合的线程安全类你知道吗

    Java并发编程之-set集合的线程安全类 Java中set集合怎么保证线程安全,这种方式你知道吗? 在Java中set集合是 本篇是<凯哥(凯哥Java:kagejava)并发编程学习> ...

随机推荐

  1. github学习(一)

    初识github篇. 一.什么是github:       GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub.       g ...

  2. C# .NET更智能的数据库操作封装项目

    前面两篇文章介绍了框架的思路及里面大概的实现过程,那时候忘记上传项目,就补发一下.顺便介绍下框架使用方式,并分析下框架使用的优缺点. 先发一下前两章的链接 篇一:http://www.cnblogs. ...

  3. MyBatis的类型自定义映射

    背景 利用MyBatis将数据库的时间类型映射成Java8的时间类型,引申对不同类型的自定义映射 实现方法 1.实现MyBatis中TypeHandler接口 @MappedTypes(value = ...

  4. 如何让celery接受定制的参数

    背景介绍 最近的一个项目使用到celery结算订单,使用celery的确很方便.但是复杂的内部框架导致了需要传人大量的参数例如数据库配置文件等.下面先来看看我仿照官网写的代码.所有代码都放到githu ...

  5. 1218: [HNOI2003]激光炸弹

    1218: [HNOI2003]激光炸弹 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1139  Solved: 542[Submit][Statu ...

  6. 作为一名JAVA程序员应该有怎样的就业思维

    想要成为合格的Java程序员或工程师到底需要具备哪些专业技能,在面试之前到底需要准备哪些东西呢?面试时面试官想了解你的什么专业技能,以下都是一个合格JAVA软件工程师所要具备的. 一.专业技能 1.熟 ...

  7. C 风格字符串相加

    <<C++ Primer>> 第四版Exercise Section 4.3.1 的4.3.0 有如下题目:编写程序连接两个C风格字符串字面值,把结果存储在C风格字符串中.代码 ...

  8. Linux学习之Vim使用

    一 为何要学Vim 所有的Unix Like系统都有自带vi编辑器 一些软件的编辑接口会自动调起vi 作为vi的升级版,vim具有程序编辑功能,而且具有代码颜色高亮显示.辨别代码的正确性等功能 以上优 ...

  9. RPC漏洞

    DCOM漏洞:利用这个漏洞攻击者只需发送特殊形式的清求到远程计算机上的135端口,轻则会造成拒绝服务攻击,严重的甚至可以让远程攻击者以本地管理员权限执行任何操作. 攻击过程:扫描也可用xscan+DC ...

  10. js代码实现放大镜效果

    每当打开淘宝,天猫等pc端时,看到心仪的物品时,点击图片时,便呈现出放大的效果.在没有去理解分析它的原理时,感觉非常的神奇,当真正地去接触,也是非常好理解.如下图展示所见: 很是常见,在此记载一下,毕 ...