Top K以及java priorityqueue

Top K问题比较常见啦,这里总结一下方法。

1、用最小堆来做。

思路是先利用数组中前k个数字建一个最小堆,然后将剩余元素与堆顶元素进行比较,如果某个元素比堆顶元素大,就替换掉堆顶元素,并且重新调整成最小堆。

到这里,堆中保存着的其实是前k个最大的数字。堆顶就是第K个最大的数字。这样前k个,第k个都可以求出来了。代码如下:

 1     public void find(int[] nums, int k){
 2         PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
 3         for ( int i = 0 ; i < k ; i ++ ) priorityQueue.offer(nums[i]);
 4         for ( int i = k ; i < nums.length ; i ++ ){
 5             if ( nums[i] > priorityQueue.peek() ){
 6                 priorityQueue.poll();
 7                 priorityQueue.offer(nums[i]);
 8             }
 9         }
10         System.out.println(priorityQueue.peek());
11         while ( priorityQueue.peek() != null ){
12             System.out.print(priorityQueue.poll()+"   ");
13         }
14     }

输出第一行是第k大的数,第二行是前k大的数。

现在有两个思考:1、这个代码是对重复数字有效的,也就是如果输入是[3,4,5,6,6],求第三大的数字,结果就是5。但是实际上我们是想忽略重复,希望输出4,应该怎么办?2、如果是想求最小的k个数字应该怎么办。

如果想忽略重复,我们在建堆和后面元素入堆的过程中增加一个判断就可以了。

如果想要求前k小或者是d第k小的数字,这里就要了解一下java对堆的封装类:priorityqueue。这个封装类默认实现最小堆并且考虑重复。如果想实现上面的思路,就要重写compare方法。compare方法在siftup的过程中使用到,siftup这个函数就是向上调整,这个函数会比较孩子节点和父亲节点的大小关系是否满足要求,如果满足,就break;如果不满足就会交换孩子和父亲的值。

 1 private void siftUp(int k, E x) {
 2     if (comparator != null)
 3         siftUpUsingComparator(k, x);
 4     else
 5         siftUpComparable(k, x);
 6 }
 7 private void siftUpUsingComparator(int k, E x) {
 8     while (k > 0) {
 9         int parent = (k - 1) >>> 1;
10         Object e = queue[parent];
11         if (comparator.compare(x, (E) e) >= 0)
12             break;
13         queue[k] = e;
14         k = parent;
15     }
16     queue[k] = x;
17 }

注意这里如果没有重写compare方法,就会调用默认的siftupComparable方法(这个方法就是构建最小堆的方法)。如果重写了,那么就会使用compare方法中的比较方法来判断是否满足条件。我重写了一个最大堆的代码如下:

1 PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
2             @Override
3             public int compare(Integer o1, Integer o2) {
4                 return o2-o1;
5             }
6         });

compare方法中o1参数是孩子节点,o2参数是父亲节点。这里最大堆的意思就是:如果父亲节点的值大于孩子节点的值,就不需要调整;否则交换。所以根据这个原则建立起的堆就是最大堆。

那么求第k个最小的数,以及前k个最小的数代码就显而易见了:

 1     public void find(int[] nums, int k){
 2         PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
 3             @Override
 4             public int compare(Integer o1, Integer o2) {
 5                 return o2-o1;
 6             }
 7         });
 8         for ( int i = 0 ; i < k ; i ++ )
 9         {
10             if ( !priorityQueue.contains(nums[i]) ) priorityQueue.offer(nums[i]);
11         }
12         for ( int i = k ; i < nums.length  ; i ++ ){
13             if ( nums[i] < priorityQueue.peek() && !priorityQueue.contains(nums[i]) ){
14                 priorityQueue.poll();
15                 priorityQueue.offer(nums[i]);
16             }
17         }
18         System.out.println(priorityQueue.peek());
19         while ( priorityQueue.peek() != null ){
20             System.out.print(priorityQueue.poll()+"   ");
21         }
22     }

原文地址:https://www.cnblogs.com/boris1221/p/9383811.html

时间: 2024-10-21 14:38:59

Top K以及java priorityqueue的相关文章

排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结束.时间复杂度:O(n^2) 选择排序:每次在无序队列中"选择"出最大值,放到有序队列的最后,并从无序队列中去除该值(具体实现略有区别).时间复杂度:O(n^2) 直接插入排序:始终定义第一个元素为有序的,将元素逐个插入到有序排列之中,其特点是要不断的 移动数据,空出一个适当的位置,把待插

Top k问题的讨论(三种方法的java实现及适用范围)

在很多的笔试和面试中,喜欢考察Top K.下面从自身的经验给出三种实现方式及实用范围. 合并法 这种方法适用于几个数组有序的情况,来求Top k.时间复杂度为O(k*m).(m:为数组的个数).具体实现如下: /** * 已知几个递减有序的m个数组,求这几个数据前k大的数 *适合采用Merge的方法,时间复杂度(O(k*m); */ import java.util.List; import java.util.Arrays; import java.util.ArrayList; public

LeetCode OJ 347. Top K Frequent Elements hashmap+排序求解

题目链接:https://leetcode.com/problems/top-k-frequent-elements/. 347. Top K Frequent Elements My Submissions QuestionEditorial Solution Total Accepted: 15510 Total Submissions: 36453 Difficulty: Medium Given a non-empty array of integers, return the k mo

Top K问题的两种解决思路

Top K问题在数据分析中非常普遍的一个问题(在面试中也经常被问到),比如: 从20亿个数字的文本中,找出最大的前100个. 解决Top K问题有两种思路, 最直观:小顶堆(大顶堆 -> 最小100个数): 较高效:Quick Select算法. LeetCode上有一个问题215. Kth Largest Element in an Array,类似于Top K问题. 1. 堆 小顶堆(min-heap)有个重要的性质--每个结点的值均不大于其左右孩子结点的值,则堆顶元素即为整个堆的最小值.J

深入理解Java PriorityQueue

深入理解Java PriorityQueue PriorityQueue 本文github地址 Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析PriorityQueue每个操作的具体过程和时间复杂度,将让读者建立对PriorityQueue建立清晰而深入的认识. 总体介绍 前面以Java ArrayDeque为例讲解了Stack和Queue,其实还有一种特殊的队列叫做PriorityQueue,即优先

Top k Largest Numbers

Given an integer array, find the top k largest numbers in it. Example Given [3,10,1000,-99,4,100] and k = 3.Return [1000, 100, 10]. 思路:由于需要按从大到小的顺序,因此直接用PriorityQueue即可,用Partition的方法的话还需要排序.直接用PriorityQueue 写的代码量少. 1 class Solution { 2 /* 3 * @param

Top k问题(线性时间选择算法)

问题描述:给定n个整数,求其中第k小的数. 分析:显然,对所有的数据进行排序,即很容易找到第k小的数.但是排序的时间复杂度较高,很难达到线性时间,哈希排序可以实现,但是需要另外的辅助空间. 这里我提供了一种方法,可以在O(n)线性时间内解决Top k问题.关于时间复杂度的证明,不再解释,读者可以查阅相关资料.具体的算法描述如下: 算法:LinearSelect(S,k) 输入:数组S[1:n]和正整数k,其中1<=k<=n: 输出:S中第k小的元素 1. If  n<20  Then  

Top k 问题

Top K的问题: 给出大量数据,找出其中前K个最大(小)的数,或者在海量数据中找到出现频率最好的前K个数. 一.给出大量数据(N个),找出其中前K个最大数(没有其他资源上的限制) 1.使用排序算法 直接使用排序算法,如快速排序,然后遍历找到最大的K个数.时间复杂度为O(NlogN): 2.部分排序 因为,只要求出前K个最大值,所以我们不必全部排好.思路是:随意选出K个数形成一个数组,然后按从大到小进行排序,再从剩下的数中,选取一个数和数组中的最小值进行比较,若小于最小值,则取下一个数继续比较:

347. Top K Frequent Elements

Given a non-empty array of integers, return the k most frequent elements. For example,Given [1,1,1,2,2,3] and k = 2, return [1,2]. Note: 347. Top K Frequent ElementsYou may assume k is always valid, 1 ≤ k ≤ number of unique elements. Your algorithm's