题目
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
思路1:
哈希表,暴力解法。
申明一个哈希表,用来存储每个值和它出现次数的映射。
遍历数组nums,将每一个值的出现的次数统计出来。
再将这个哈希表转成数组,对数组做排序处理,然后将数组前k项的值return出来即可
代码如下:
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
let map = new Map();
for(const num of nums) {
map.set(num, (map.get(num) || 0) + 1);
}
var arr=Array.from(map);
arr.sort(function(a,b){return b[1]-a[1]})
let res = [];
for(let i=0;i<k;i++){
res.push(arr[i][0])
}
return res
};
复杂度分析
时间复杂度:O(n^2),最坏的情况,每个元素都只出现一次,那么排序这里耗时n^2
空间复杂度:O(n),最坏的情况,每个元素只出现一次,map的size即位数组的长度。
思路2:
小顶堆。
首先,还是要像上面一样,申明一个哈希表,将值与它出现的次数维护在这个哈希表里。
然后我们申明一个小顶堆,for of遍历这个哈希表的entries,此时entry会是一个键值对数组,键为数值,值为数值出现的次数。
- 将每个键值数组推入小顶堆
- 入堆后,小顶堆自动会排序,我们让它以出现次数排序,那么出现次数最少的值会浮到最上面
- 持续推入操作,当小顶堆的节点数大于k时,说明此时堆中刚好有k+1个元素,那么我们将堆顶元素弹出,这时堆内就是刚好出现频率最高的k个元素了。
实现代码如下:
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
const map = new Map();
const res = [];
for(const num of nums) {
map.set(num, (map.get(num) || 0) + 1);
}
const heap = new MinHeap();
for (const entry of map.entries()) {
heap.offer(entry);
if (heap.size() > k) {
heap.pop();
}
}
for(let i = heap.size() - 1; i >= 0; i--) {
res[i] = heap.pop()[0];
}
return res;
};
class MinHeap{
constructor(){
this.heap = [];
}
offer(item){
this.heap.push(item);
let index = this.heap.length - 1;
let parent = Math.floor((index - 1) / 2);
while(parent >= 0 && this.compare(parent, index) > 0) {
[this.heap[index], this.heap[parent]] = [this.heap[parent], this.heap[index]];
index = parent;
parent = Math.floor((index - 1) / 2);
}
}
pop(){
const res = this.heap[0];
this.heap[0] = this.heap.pop();
let index = 0,left = 1;
let child = this.compare(left, left + 1) > 0 ? left + 1 : left;
while(child !== undefined && this.compare(index, child) > 0) {
[this.heap[index], this.heap[child]] = [this.heap[child], this.heap[index]];
index = child;
left = 2 * index + 1;
child = this.compare(left, left + 1) > 0 ? left + 1 : left;
}
return res;
}
size(){
return this.heap.length;
}
compare(index1,index2){
if (this.heap[index1] === undefined) return 1
if (this.heap[index2] === undefined) return -1
return this.heap[index1][1]>this.heap[index2][1];
}
}
复杂度分析:
时间复杂度:O(nlogk),遍历得到哈希表是O(n),随后处理哈希表为O(n),每次遍历,还都会执行小顶堆排序,最后一次为复杂度为O(logk),那么处理哈希表并执行堆排序为O(nlogk),最终取O(nlogk);
空间复杂度:O(n),哈希表空间复杂度为O(n),堆为O(k),取O(n)