「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n)
,其中 n
**是数组大小。
本题需要找到前 k
个高频元素,通常维护前 k
个最值的问题,都可以通过堆来解决
堆的性质
堆的插入
插入堆就是在数组的末尾插入元素
然后判断新插入元素是否影响了当前三元组的平衡。以大顶堆为例,如果插入元素的值大于根节点的值,则交换两个节点,直到新插入元素所在三元组满足根节点的值为当前三元组最值为止
堆的弹出
堆的弹出操作指弹出堆顶元素,对应数组下标 0
的元素,此时更新数组末尾元素为数组第一个元素,并删除之前末尾元素
然后判断新的堆顶元素是否影响了当前三元组的平衡。以大顶堆为例,如果新的堆顶元素不是当前三元组最大值,则与当前三元组最大值进行交换,知道它成为新的三元组的最大值为止
题解1
理解了堆的性质,我们来看一下本题的第一种题解
- 使用
map
维护数组中元素的值以及出现的次数 - 创建一个小顶堆用来维护前
k
个高频元素 - 遍历
map
,如果堆中的元素大于等于k
个,并且堆顶元素大于本次循环数据的val
值,不进行插入,否则本次循环数据插入堆中 - 当堆不为空的时候,将堆顶元素弹出,把弹出值的
key
放到结果数组中,最后返回结果数组即可
代码如下:
class MinHeap {
constructor(max){
this.arr = [];
this.size = 0;
this.max = max;
}
// 插入元素
push(val){
this.arr.push(val);
this.size++;
if(this.size>1){
let cur = this.size-1,
parentInd = (cur-1)>>1;
while(cur>0 && this.arr[parentInd].val>this.arr[cur].val){
[this.arr[parentInd],this.arr[cur]] =
[this.arr[cur],this.arr[parentInd]]
cur = parentInd;
parentInd = (cur-1)>>1;
}
}
while(this.size>this.max) this.pop();
}
// 弹出堆顶元素
pop(){
if(this.empty()) return false;
const res = this.arr[0]
this.arr[0] = this.arr.pop();
this.size--;
let cur = 0,
childl = 1,
childr = 2;
while(
(childl<this.size&&this.arr[childl].val<this.arr[cur].val) ||
(childr<this.size&&this.arr[childr].val<this.arr[cur].val)
){
if(childr<this.size&&this.arr[childr].val<this.arr[childl].val){
[this.arr[cur],this.arr[childr]] =
[this.arr[childr],this.arr[cur]]
cur = childr;
}else{
[this.arr[cur],this.arr[childl]] =
[this.arr[childl],this.arr[cur]]
cur = childl;
}
childl =cur*2+1,childr = cur*2+2;
}
return res;
}
// 判断堆是否为空
empty(){
return this.size === 0
}
// 返回堆顶元素
top(){
return this.arr[0]
}
}
var topKFrequent = function(nums, k) {
// map 维护数组中的值以及出现的次数
const map = new Map();
for(let i = 0;i<nums.length;i++){
const cur = nums[i]
if(map.has(cur)){
map.set(cur,map.get(cur)+1)
}else{
map.set(cur,1)
}
}
// 创建小顶堆维护前k个高频元素
const heap = new MinHeap(k);
map.forEach((val,key) => {
if(heap.size<k || heap.top().val<val){
heap.push({val,key})
}
})
// 将堆中元素放入结果数组
const ret = [];
while(!heap.empty()){
ret.push(heap.pop().key)
}
return ret;
};
题解2
- 使用
map
维护数组中元素的值以及出现的次数 - 通过
Array.from
将map
转为数组 - 根据元素出现的次数进行降序排序
- 将前k个高频元素的值放到结果数组
代码如下:
var topKFrequent = function(nums, k) {
// map 维护数组中的值以及出现的次数
const map = new Map();
for(let i = 0;i<nums.length;i++){
const cur = nums[i]
if(map.has(cur)){
map.set(cur,map.get(cur)+1)
}else{
map.set(cur,1)
}
}
// 将map转为数组
const arr = Array.from(map);
// 根据元素出现的次数进行降序排序
arr.sort((a,b) => b[1]-a[1])
// 将前k个高频元素的值放到结果数组
const ret = [];
for(let i = 0;i<k;i++){
ret.push(arr[i][0])
}
return ret;
};
至此,我们就通过两种方式完成了 leetcode-347-前K个高频元素
如有任何问题或建议,欢迎留言讨论!