题目
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
解题思路
本题和上一题基本类似,都可以使用小顶堆处理,只是上一题第一步是对大小进行排序,构建小顶堆的时候可以借助堆的特性直接得出结果,而本题需要对出现频率进行排序。排序方式很简单,只需要在遍历数组的时候记录频率即可。
我们可以使用map
在遍历的过程中记录元素值和出现的频率。然后遍历map
取前k
个数作构建小顶堆。剩下的步骤就跟上题一致了,从k
位开始继续遍历,比较该元素与堆顶元素出现的频率,如果出现的频率高,就直接替换掉堆顶元素,进行堆化,否则不做处理。
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
var map = new Map(),heap = [,];
// 记录频率
nums.map(function(num){
if(map.has(num)){
map.set(num,map.get(num) + 1);
}else{
map.set(num,1)
}
})
// 如果元素数量小于k
if(map.size <= k){
return [...map.keys()]
}
// 如果元素数量大于k 则遍历map
var i = 0; // 记录插入个数
map.forEach(function(value,key){
if(i < k){
heap.push(key);
// 满足k个之后,原地建堆
if(i === k -1){
buildHeap(heap,map,k)
}
// 如果k个元素之后的元素出现次数大于堆顶 则替换
}else if(map.get(heap[1]) < value){
heap[i] = key
// 堆化
heapify(heap,map,k,1)
}
i++
})
// 删除 堆中第一个占位
heap.shift()
return heap
};
// 原地建堆
var buildHeap = function(heap,map,k){
if(k = 1) return;
//从最后一个非叶子节点开始,自上而下堆化
for(var i = Math.floor(k/2);i>=1;i--){
heapify(heap,map,k,i);
}
}
//堆化
var heapify = function(heap,map,k,i){
while(true){
// 假设i元素最小
var minindex = i
if(2*i <= k && map.get(heap[2*i]) < map.get(heap[i])) {
minIndex = 2*i
}
if(2*i+1 <= k && map.get(heap[2*i+1]) < map.get(heap[minIndex])) {
minIndex = 2*i+1
}
if(minIndex !== i) {
swap(heap, i, minIndex)
i = minIndex
} else {
break
}
}
}
// 交换
let swap = (arr, i , j) => {
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}