java解决TopK问题
程序员文章站
2022-05-15 09:16:27
...
TopK问题描述
在不知道有多少元素,或者说元素个数不固定、很多时,普通的排序方式就不能再使用了,效率低下。
本文将使用java的api中的优先级队列,实现最大的topK和最小的topK。
也就是说求出大量数据中的前k个,可以是从大到小的topN,也可以是从小到大的topN。
java代码
package org.feng.queue;
import com.sun.istack.internal.NotNull;
import java.util.*;
import java.util.function.Consumer;
/**
* Created by Feng on 2019/12/30 9:57
* CurrentProject's name is java8
* 使用 java 中的{@link PriorityQueue}来完成 TopK 计算
* @see Comparable 是应该要存储的内容,保证有序
* @see PriorityQueue 核心数据结构
* @author Feng
*/
public class TopK<E extends Comparable> implements Iterable<E> {
private PriorityQueue<E> priorityQueue;
/**top的大小*/
private int k;
private Order order;
/**
* 迭代:拿到底层优先级队列的迭代器
* @return Iterator
* @see PriorityQueue
*/
@Override
public Iterator<E> iterator() {
return priorityQueue.iterator();
}
@SuppressWarnings("unchecked")
@Override
public void forEach(Consumer action) {
Objects.requireNonNull(action);
for (E e: this) {
action.accept(e);
}
}
/**
* 构造器:指定堆(优先级队列)的大小
* @param k 大小
*/
public TopK(int k){
this(Order.MAX, k);
}
/**
* 按照指定的 {@link Order}类型来确定是最大值的 TopK 还是最小值的 TopK
* @param order {@code Order.MIN 表示最小;Order.MAX表示最大}
* @param k 大小
*/
public TopK(Order order, int k){
this.k = k;
this.order = order;
if(Order.MIN.equals(order)){
this.priorityQueue = new PriorityQueue<>(k, Collections.reverseOrder());
} else {
this.priorityQueue = new PriorityQueue<>(k);
}
}
/**
* 批量增加元素
* @param collection 集合
*/
public void addAll(Collection<? extends E> collection){
if(Order.MIN.equals(order)){
for (E e : collection) {
addMin(e);
}
return;
}
for (E e : collection) {
addMax(e);
}
}
/**
* 动态添加元素:tops 中始终是最大的 k 个元素
* <p>
* 1. 如果元素个数小于优先级队列的大小,则直接添加<br>
* 2. 当元素个数不小于优先级队列大小时,先与队列中最小值比较,
* 如果大于队列中最小值,则替换掉这个最小值;否则就不做操作。
* </p>
* @param e 元素,假定其实现了 {@link Comparable} 接口
*/
@SuppressWarnings("unchecked")
public void addMax(E e) {
if(insert(e)){
return;
}
E head = priorityQueue.peek();
// 队列中的元素小于要增加的元素
if(Objects.requireNonNull(head).compareTo(e) <= 0){
// 新的元素替换掉原来的最小值,成为Topk中的值
priorityQueue.poll();
priorityQueue.add(e);
}
}
/**
* 最小值的 top 队列
* @param e
*/
@SuppressWarnings("unchecked")
public void addMin(E e){
if(insert(e)){
return;
}
E head = priorityQueue.peek();
if(Objects.requireNonNull(head).compareTo(e) > 0){
// 新的元素替换掉原来的最大值,成为Topk中的值
priorityQueue.poll();
priorityQueue.add(e);
}
}
/**
* 队列未满时,直接插入数据
* @param e 数据
* @return 插入成功返回 true
*/
public boolean insert(E e){
if(priorityQueue.size() < k){
return priorityQueue.add(e);
}
return false;
}
public <T>T[] toArray(T[] arr){
return priorityQueue.toArray(arr);
}
public <T>T[] toArraySorted(T[] arr){
T[] array = priorityQueue.toArray(arr);
Arrays.sort(array);
return array;
}
public <T>T[] toArraySorted(T[] arr, @NotNull Comparator<T> comparator){
Objects.requireNonNull(comparator);
T[] array = priorityQueue.toArray(arr);
Arrays.sort(array, comparator);
return array;
}
/**
* 获取第 k 个最大或最小的元素
* @return E
*/
public E getKth(){
return priorityQueue.peek();
}
@Override
public String toString() {
String orderString = Order.MIN.equals(order) ? "min" : "max";
return "TopK[k = " + k + ", order = " + orderString + "] ,Tops" + priorityQueue.toString();
}
/**
* 排序规则:正序和逆序
*/
public enum Order{
/**逆序:从小到大排序*/
MIN,
/**正序:从大到小排序*/
MAX
}
}
测试
package org.feng.queue;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* Created by Feng on 2019/12/30 10:23
* CurrentProject's name is java8
* @author Feng
*/
public class TopTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 3, 5, 11, 6, 77, 23, 43, 32, 8, 99);
System.out.println("min:");
// 设置大小为5
TopK<Integer> minTop = new TopK<>(TopK.Order.MIN,5);
// 添加元素
minTop.addAll(list);
// TopK[k = 5 order = reverse] ,Tops[8, 6, 3, 2, 5]
System.out.println(minTop);
minTop.forEach(System.out::println);
System.out.println("max:");
TopK<Integer> maxTop = new TopK<>(TopK.Order.MAX,5);
maxTop.addAll(list);
System.out.println(maxTop);
maxTop.forEach(System.out::println);
System.out.println("toArray:");
Integer[] array = maxTop.toArray(new Integer[0]);
System.out.println(Arrays.toString(array));
System.out.println("toArraySorted1:");
Integer[] sorted1 = maxTop.toArraySorted(new Integer[0]);
System.out.println(Arrays.toString(sorted1));
System.out.println("toArraySorted2:");
Integer[] sorted2 = maxTop.toArraySorted(new Integer[0], Comparator.comparingInt(n -> n));
System.out.println(Arrays.toString(sorted2));
}
}
测试结果
控制台打印了:
min:
TopK[k = 5, order = min] ,Tops[8, 6, 3, 2, 5]
8
6
3
2
5
max:
TopK[k = 5, order = max] ,Tops[23, 32, 77, 43, 99]
23
32
77
43
99
toArray:
[23, 32, 77, 43, 99]
toArraySorted1:
[23, 32, 43, 77, 99]
toArraySorted2:
[23, 32, 43, 77, 99]
Process finished with exit code 0
推荐阅读
-
java环境变量配置好后双击jar文件无法运行的解决办法
-
解决eclipse中egit中的cannot open git-upload-pack问题
-
Adobe Dreamweaver CC完美破解补丁amtlib.dll 解决进程CPU占用高问题
-
illegal opcode 红屏报错(hp 360 G6安装win2003)问题解决方法
-
在笔记本中Virtual PC 2007的问题及解决方案
-
Mysql数据库从5.6.28版本升到8.0.11版本部署项目时遇到的问题及解决方法
-
解决nohup执行python程序log文件写入不及时的问题
-
VS2010无法启动调试问题解决方法小结
-
移动网站如何做才符合用户搜索 这个问题必须要解决
-
如何使用加速人生一键解决网游延时问题