在 Java 中,寻找 最大 Top K 和 最小 Top K 的元素通常使用 优先队列(PriorityQueue)。具体来说:
- 最大 Top K:用一个小根堆(
PriorityQueue默认是小根堆)保存当前最大的 K 个元素。 - 最小 Top K:用一个大根堆(通过
Comparator实现)保存当前最小的 K 个元素。
核心思路
-
小根堆:用于维护最大 Top K。
- 堆中始终保留 K 个元素,当新元素大于堆顶时替换堆顶,重新调整堆。
- 堆顶始终是当前 K 个最大元素中的最小值。
-
大根堆:用于维护最小 Top K。
- 堆中始终保留 K 个元素,当新元素小于堆顶时替换堆顶,重新调整堆。
- 堆顶始终是当前 K 个最小元素中的最大值。
代码实现
1. 最大 Top K(使用小根堆)
import java.util.PriorityQueue;
public class TopKMax {
public static void main(String[] args) {
int[] nums = {3, 2, 1, 5, 6, 4, 8, 9, 7};
int k = 3; // 找最大的 Top 3
PriorityQueue<Integer> minHeap = new PriorityQueue<>(k); // 小根堆
for (int num : nums) {
if (minHeap.size() < k) {
minHeap.offer(num); // 堆未满,直接加入
} else if (num > minHeap.peek()) {
minHeap.poll(); // 弹出最小值
minHeap.offer(num); // 插入新元素
}
}
// 输出结果:堆中存储的就是最大 Top K
System.out.println("最大 Top " + k + ": " + minHeap);
}
}
关键点解析
PriorityQueue<Integer>默认是小根堆,peek()获取堆顶(最小值)。- 每次插入新元素时,堆中只保留 K 个最大的元素,堆顶是当前 Top K 最大中的最小值。
输出示例
最大 Top 3: [7, 8, 9]
2. 最小 Top K(使用大根堆)
import java.util.PriorityQueue;
import java.util.Collections;
public class TopKMin {
public static void main(String[] args) {
int[] nums = {3, 2, 1, 5, 6, 4, 8, 9, 7};
int k = 3; // 找最小的 Top 3
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, Collections.reverseOrder()); // 大根堆
for (int num : nums) {
if (maxHeap.size() < k) {
maxHeap.offer(num); // 堆未满,直接加入
} else if (num < maxHeap.peek()) {
maxHeap.poll(); // 弹出最大值
maxHeap.offer(num); // 插入新元素
}
}
// 输出结果:堆中存储的就是最小 Top K
System.out.println("最小 Top " + k + ": " + maxHeap);
}
}
关键点解析
PriorityQueue<>(k, Collections.reverseOrder())创建大根堆。- 每次插入新元素时,堆中只保留 K 个最小的元素,堆顶是当前 Top K 最小中的最大值。
输出示例
最小 Top 3: [2, 1, 3]
3. 扩展:返回结果的排序
默认情况下,堆中的元素顺序不保证是从大到小或从小到大。如果需要结果有序,可以将堆元素导出后排序:
对最大 Top K 排序
import java.util.*;
public class Main {
public static void main(String[] args) {
int[] nums = {3, 2, 1, 5, 6, 4, 8, 9, 7};
int k = 3;
PriorityQueue<Integer> minHeap = new PriorityQueue<>(k);
for (int num : nums) {
if (minHeap.size() < k) {
minHeap.offer(num);
} else if (num > minHeap.peek()) {
minHeap.poll();
minHeap.offer(num);
}
}
// 转换为有序结果
List<Integer> result = new ArrayList<>(minHeap);
Collections.sort(result, Collections.reverseOrder()); // 从大到小排序
System.out.println("最大 Top " + k + " (有序): " + result);
}
}
输出示例
最大 Top 3 (有序): [9, 8, 7]
时间复杂度分析
假设数组大小为 nnn,需要找 Top kkk 个元素:
-
堆操作:
- 插入(
offer)和删除(poll)的时间复杂度为 O(logk)O(\log k)O(logk)。 - 对于 nnn 个元素,最多进行 nnn 次堆操作,时间复杂度为 O(nlogk)O(n \log k)O(nlogk)。
- 插入(
-
排序(可选):
- 如果需要对结果排序,复杂度为 O(klogk)O(k \log k)O(klogk)。
-
总复杂度:
- 不需要排序:O(nlogk)O(n \log k)O(nlogk)。
- 需要排序:O(nlogk+klogk)O(n \log k + k \log k)O(nlogk+klogk)。
总结
-
最大 Top K(小根堆):
- 使用
PriorityQueue默认实现,堆顶是当前 K 个最大值中的最小值。
- 使用
-
最小 Top K(大根堆):
- 使用
PriorityQueue+Collections.reverseOrder()创建大根堆,堆顶是当前 K 个最小值中的最大值。
- 使用
-
效率:
- 堆的实现非常高效,适合处理大规模数据。
- 若 K 远小于 N,使用堆的方式比全排序更优。
-
排序结果:
- 如果需要有序的 Top K 结果,可以将堆内容导出到列表并排序。


3822

被折叠的 条评论
为什么被折叠?



