top k问题:从10亿个数中选出最大的1万个数,处理方式:用小顶堆,先用1万个数建立小顶堆,再把剩余数从小顶堆里过一遍,每次与堆顶元素比较,小顶堆的堆顶元素是最小的,如果比堆顶元素大就替换堆顶元素,重新生成小顶堆,继续比较直到10亿条数据比完,堆里剩下的就是最大的1万个数。
如果是从大量元素里挑出最小的前k个,就建立容量为k的大顶堆,堆顶是最大的元素,剩余元素跟堆顶元素比较,如果堆顶元素大就替换掉。
大顶堆,小顶堆如何建立,在java代码中有现成的堆数据结构PriorityQueue(也可以用数组自己建立堆数据结构,替换堆顶元素重新排序需要自己写方法实现),默认是小顶堆,想要大顶堆就重写compare方法
一: 用PriorityQueue找出前k个最小元素:
public static int[] topK(int[] arr,int k){ // 创建一个大小为 k的大根堆 PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k,new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); for (int i = 0; i < arr.length; i++) { if (i < k){ // 放入前 k 个元素 maxHeap.offer(arr[i]); }else{ // 从第 k+1个元素开始进行判断是否要入堆 if (maxHeap.peek() > arr[i]){ maxHeap.poll(); maxHeap.offer(arr[i]); } } } int[] ret = new int[k]; for (int i = 0; i < k; i++) { ret[i] = maxHeap.poll(); } return ret; }
二 top k找出出现频率最大的前k条:
先用hash结构获取元素值和出现的次数,再将hash结构放到堆中用频次比较:
public int[]topKFrequent(int[]nums,int k){ //occurrences的key是元素值,value是元素出现的次数 Map<Integer,Integer> occurrences = new HashMap<Integer,Integer>(); for(int num:nums){ //occurrences.getOrDefault(key,def)是根据key获取value值,如果找不到key就返回默认值def //第一次occurrences为空都找不到,值是1,后面能找到了每次加一 occurrences.put(num,occurrences.getOrDefault(num,0)+1); } PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>(){ //PriorityQueue是Java的堆数据结构,这里存放的是整形数组,下标0存key,下标1存出现次数 //重写compare方法,指定用频次比较 public int compare(int[]m,int[]n){return m[1]- n[1];} }); for(Map.Entry<Integer,Integer>entry : occurrences.entrySet()){ int num=entry.getKey(),count=entry.getValue(); if(queue.size()==k){ if(queue.peek()[1]<count) {//堆顶元素下标1是频次,和数组的其他元素的频次比较 //删除堆顶元素 queue.poll(); //堆中加入元素 queue.offer(new int[]{num, count}); } }else{ //堆中加入元素 queue.offer(new int[]{num, count}); } } int[]ret = new int[k]; for(int i=0;i<k; ++i){ ret[i]= queue.poll()[1]; } return ret; }
标签:maxHeap,Java,int,top,元素,PriorityQueue,occurrences,new,代码 From: https://www.cnblogs.com/1--2/p/18175321