解题思路
347. 前 K 个高频元素
大体思路:
①先要计算出各数字所出现的频次。数字:频次。可见这是一个键值对形式,所以可以用双列集合Map来存储。
② 找到频次前K高个数值
对于②的实现有很多方法,下面给出了三种解法。
- 使用数组进行全排序
- 使用优先级队列进行排序
- 使用桶排序
代码
方法一:全排序
//运行时间:17 ms 内存消耗:41.2MB
import java.util.Map.Entry;
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//利用Map存储每个整数出现的次数
Map<Integer , Integer> counts = new HashMap<>();
for(int num : nums){
counts.put(num,counts.getOrDefault(num,0)+1);
}
//Entry是Map中的 键值对 对象
Entry<Integer , Integer>[] entries = new Entry[counts.size()];
Set entry_set = counts.entrySet();
entry_set.toArray(entries); //把set转换为数组
Arrays.sort( //对数组按出现频度进行从大到小的排序
entries,
(Entry<Integer , Integer> e1 , Entry<Integer , Integer> e2) -> e2.getValue()-e1.getValue()
);
int[] result = new int[k];
for(int i=0 ; i<k ; i++){
result[i] = entries[i].getKey();
}
return result;
}
}
方法二:使用优先级队列:PriorityQueue
//运行时间:15 ms 内存消耗:41 MB
import java.util.Map.Entry;
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//利用Map存储每个整数出现的次数
Map<Integer , Integer> counts = new HashMap<>();
for(int num : nums){
counts.put(num,counts.getOrDefault(num,0)+1);
}
//Entry是Map中的 键值对 对象
Set<Entry<Integer , Integer>> entry_set = counts.entrySet();//泛型可加也可不加
PriorityQueue<Entry<Integer , Integer>> queue = new PriorityQueue<>(
(Entry<Integer , Integer> e1 , Entry<Integer , Integer> e2) -> e1.getValue()-e2.getValue()
);
for(Entry<Integer , Integer> entry : entry_set){
if(queue.size()<k){
queue.offer(entry);
}else if(entry.getValue()>queue.peek().getValue()){ //peek():取出堆顶元素
queue.poll();
queue.offer(entry);
}
}
int[] result = new int[k];
while(!queue.isEmpty()){
result[--k] = queue.poll().getKey();
}
return result;
}
}
方法三:桶排序
//运行时间:13ms 内存消耗:41.2MB
import java.util.Map.Entry;
//方法二:使用桶排序
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//利用Map存储每个整数出现的次数
Map<Integer , Integer> counts = new HashMap<>();
for(int num : nums){
counts.put(num,counts.getOrDefault(num,0)+1);
}
//Entry是Map中的 键值对 对象
Set<Entry<Integer , Integer>> entry_set = counts.entrySet();//泛型可加也可不加
//创建一个nums.length长度的集合,里面存放所有的桶,出现频次为n的数值放到[n-1]号桶里
List<Integer>[] buckets = new List[nums.length ];
int maxBucket = 0; //记录最大频次所在的桶的位置
for(Entry<Integer , Integer> entry : entry_set){
int frequency = entry.getValue();
List<Integer> bucket = buckets[frequency-1];
if(bucket == null){
bucket = new LinkedList<>();
buckets[frequency-1] = bucket;
maxBucket = Math.max(maxBucket , frequency-1);
}
bucket.add(entry.getKey());
}
int[] result = new int[k];
int j=0;
for(int i=maxBucket ; i>=0&&j<k; i--){ //从后往前遍历,拿了k个值就终止
if(buckets[i] == null) continue;
for(Integer number : buckets[i]){
result[j++] = number;
}
}
return result;
}
}