【Java】TOP-K问题

TOP-K问题

题目

https://leetcode.cn/problems/smallest-k-lcci/description/

算法的思路详解

整体排序法

核心思路

既然要找最小的前 K 个元素,那就干脆把所有元素排好序,然后直接取前 K 个。

执行步骤

  1. 将整个数组从小到大排序
  2. 取出排序后数组的前 K 个元素

优点 :代码最简单,思路最直接
缺点:做了很多"无用功"------我们只需要前 K 小,却把整个数组都排好了

整体建立小顶堆法

核心思路

用小顶堆的特性------堆顶永远是最小值。把所有元素放入堆中,然后依次弹出堆顶,弹出来的就是从小到大的顺序。

执行步骤

  1. 把所有 N 个元素放入一个小顶堆(建堆 O(N))
  2. 从堆顶弹出元素,弹出来的就是当前最小值
  3. 重复弹出 K 次,得到前 K 小的元素

优点 :比排序法稍快(建堆 O(N) vs 排序 O(N log N))
缺点:仍然需要存储全部 N 个元素

大小为 K 的大顶堆法

核心思路

维护一个"门槛"------只关心前 K 小的元素,大于门槛的统统不要。用大顶堆来记录当前找到的 K 个候选,堆顶是这 K 个里面最大的(也就是当前的"门槛")。

执行步骤

  1. 先用前 K 个元素建一个大顶堆(堆顶是这 K 个中最大的)
  2. 遍历剩下的 N-K 个元素:
    • 如果当前元素 小于 堆顶(门槛),说明它应该进入前 K 小
    • 把堆顶(最大的那个)踢出去,把当前元素加进来
  3. 遍历结束后,堆里剩下的就是前 K 小的元素

为什么用大顶堆?

因为我们要快速知道当前 K 个候选中的最大值是谁,新来的只要比它小,就能替换掉它。

优点 :内存占用极小(只存 K 个元素),适合海量数据(比如 10 亿个数找前 100 小)
缺点:实现稍复杂

code

整体排序法

java 复制代码
// 1. 整体排序法
 public static List<Integer> topKBySorting(int[] arr, int k) {
     if (arr == null || arr.length == 0 || k <= 0) return new ArrayList<>();
     int[] copy = Arrays.copyOf(arr, arr.length);
     Arrays.sort(copy);
     List<Integer> result = new ArrayList<>();
     for (int i = 0; i < k && i < copy.length; i++) {
         result.add(copy[i]);
     }
     return result;
 }

整体建立小顶堆法

java 复制代码
// 2. 整体建立小顶堆法(把所有元素放入小顶堆,再弹出前K个)
public int[] smallestK(int[] arr, int k) {
   int []result=new int [k];

   if (arr == null || arr.length == 0 || k <= 0) return result;

   PriorityQueue<Integer> heap = new PriorityQueue<>(); // 小顶堆
   for (int num : arr) {
       heap.offer(num);
   }

   for (int i = 0; i < k ; i++) {
       result[i]=(heap.poll());
   }
   return result;
    
}

大小为K的大顶堆法

java 复制代码
// 3. 大小为K的大顶堆法(推荐,适合大数据量)

class IntCmp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2.compareTo(o1);
    }
}
class Solution {
    public int[] smallestK(int[] arr, int k) {
       int []result=new int [k];

       if (arr == null || arr.length == 0 || k <= 0) return result;

       PriorityQueue<Integer> heap = new PriorityQueue<>(new IntCmp()); // 大顶堆

       for (int i=0;i< k; i++) {
            heap.offer(arr[i]);
       }

       for(int j=k;j<arr.length;j++){
            if(heap.peek()>arr[j]){
                heap.poll();
                heap.offer(arr[j]);
            }
       }

       for (int l = 0; l < k ; l++) {
           result[l]=(heap.poll());
       }
       return result;
        
    }
}
 

三种方法对比

方法 思路
整体排序 全部排好序,再取前 K 个
整体小顶堆 所有元素进堆,再弹出 K 次
大小为 K 的大顶堆 维持一个 K 大小的"候选池",池里最大的就是门槛
方法 时间复杂度 空间复杂度 说明
整体排序 O(N log N) O(N) 或 O(log N)(原地排序) 简单直接,但不需要全部排序
整体小顶堆 O(N + K log N) O(N) 建立堆 O(N),弹出 K 次 O(K log N)
大小为 K 的大顶堆 O(N log K) O(K) 最适合流式/海量数据,内存占用最小
相关推荐
鲸采云SRM采购管理系统22 分钟前
采购管理系统哪家好?2026最新测评对比
java·服务器·数据库
C++ 老炮儿的技术栈43 分钟前
基于Qt实现轻量化本地音乐播放器
开发语言·c++·qt·c·播放器·音乐
qq_448011161 小时前
C语言中的柔性数组
c语言·开发语言·柔性数组
小白学大数据1 小时前
长周期爬虫的数据一致性:断点续爬 + 事务回滚保障采集质量
开发语言·爬虫·测试工具
0x531 小时前
Java网络编程(二)
java·服务器·网络
zlinear数据采集卡1 小时前
D223上位机C#开发实战:从帧解析到波形显示的完整实现
开发语言·arm开发·嵌入式硬件·fpga开发·开源·c#
Flittly2 小时前
【雕虫大技】Agent 动态 Skill 供应链安全加固(三):输出校验与治理闭环实战
java·spring boot·spring
Poo_Chai2 小时前
QT emit信号后完整处理流程,包括槽函数响应流程
java·开发语言·数据库
瑞码空间2 小时前
Java 图形界面(GUI)完整知识点手册
java·开发语言·图形界面·swing
树码小子2 小时前
开启 IDEA 中的断言功能
android·java·intellij-idea