Top-k问题
给定一个长度为k的无序数组 nums ,请返回数组中最大的个元素。
1. 方法一:遍历选择
可以进行k轮遍历,分别在每轮中提取第 1、2、...、k 大的元素,时间复杂度为 O(nk)
此方法只适用于 k<=n 的情况,因为当 k与n 比较接近时,其时间复杂度趋向于 O(n²) ,非常耗时
2. 方法二:排序
可以先对数组 nums 进行排序,再返回最右边的k个元素,时间复杂度为 O(nlogn)
显然,该方法超额完成任务,因为只需要找出最大的 k 个元素即可,而不需要排序其他元素
3. 方法三:堆
-
初始化一个小顶堆,其堆顶元素最小
-
先将数组的前 k 个元素依次入堆
-
从第 k+1 个元素开始,若当前元素大于堆顶元素,则将堆顶元素出堆,并将当前元素入堆
-
遍历完成后,堆中保存的就是最大的 k 个元素
/* 基于堆查找数组中最大的 k 个元素 */ Queue<Integer> topKHeap(int[] nums, int k) { // 初始化小顶堆 Queue<Integer> heap = new PriorityQueue<Integer>(); // 将数组的前 k 个元素入堆 for (int i = 0; i < k; i++) { heap.offer(nums[i]); } // 从第 k+1 个元素开始,保持堆的长度为 k for (int i = k; i < nums.length; i++) { // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 if (nums[i] > heap.peek()) { heap.poll(); heap.offer(nums[i]); } } return heap; }
总共执行了 n 轮入堆和出堆,堆的最大长度为 k ,因此时间复杂度为 O(nlogk)
当 k 较小时,时间复杂度趋向于 O(n) ; 当 k 较大时,时间复杂度不会超过 O(nlogn)
另外,该方法适用于动态数据流的使用场景,在不断加入数据时,可与持续维护堆内的元素,从而实现最大的 k 个元素的动态更新