「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」
题目介绍
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例1
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例2
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 10^5k的取值范围是[1, 数组中不相同的元素的个数]- 题目数据保证答案唯一,换句话说,数组中前
k个高频元素的集合是唯一的 进阶: 你所设计算法的时间复杂度 必须 优于O(n log n),其中n**是数组大小。
解题思路
题目要求返回出现频率前 k 高的元素,因此需要先遍历一次整个数组中的元素,记录每个元素出现的次数,为了提高效率,可以使用 Map 来存储每个元素出现的次数
这是第一步,这一步是不可避免的,每一种解法都需要先进行这一步,接下去就是排序的方法,下面讲两种解法
思路一:直接排序
直接排序是在统计完每个元素出现的次数之后直接利用 sort 方法进行频率从高到低的排序,然后截取排序之后数组的前 k 位
解题步骤
- 遍历整个数组,将每个元素的次数记录在 Map 结构中
- 对
map.keys()按出现的次数从高到低进行排序 - 返回排序后数组的前
k位
解题代码
var topKFrequent = function(nums, k) {
const map = new Map()
nums.map(v => {
if(!map.has(v)) {
map.set(v, 1)
} else {
map.set(v, map.get(v) + 1)
}
})
const arr = [...map.keys()]
arr.sort((a, b) => map.get(b) - map.get(a))
return arr.slice(0, k)
};
思路二:小顶堆
对于这种返回前 k 大类型的题目,都可以使用堆来做,维护一个大小为 k 的小顶堆,然后将每个元素及其出现的次数插入到小顶堆中,最后返回小顶堆中的元素就可以了
因为这道题没有规定我们返回的顺序,所以最后我们可以直接返回堆中的所有元素,不需要进行排序
解题步骤
- 遍历整个数组,将每个元素的次数记录在 Map 结构中
- 维护一个大小为
k的小顶堆minHeap - 依次将
map.entries()中的每个键值对插入到小顶堆中 - 返回小顶堆中的所有元素的键组成的数组
解题代码
var topKFrequent = function(nums, k) {
const map = new Map()
nums.map(v => {
if (!map.has(v)) {
map.set(v, 1)
} else {
map.set(v, map.get(v) + 1)
}
})
const minHeap = new Heap(k)
// 将所有的键值对插入到小顶堆中
for(let item of map.entries()) {
minHeap.push(item)
}
// 小顶堆中保存的元素即为频率前 k 高的元素
return minHeap.toArray()
};
class Heap {
constructor(k) {
this.k = k
this.arr = []
}
// 直接返回小顶堆中所有元素的键
toArray() {
return this.arr.map(v => {return v[0]})
}
// 向小顶堆中插入元素
push(val) {
if (this.arr.length < this.k) {
this.arr.push(val)
this._sortBack()
} else if (val[1] > this.arr[0][1]) {
this.arr[0] = val
this._sortFront()
}
}
// 从底部向上调整堆结构
_sortBack() {
let i = this.arr.length - 1
while(i > 0 && this.arr[Math.floor((i - 1) / 2)][1] > this.arr[i][1]) {
[this.arr[Math.floor((i - 1) / 2)], this.arr[i]] = [this.arr[i], this.arr[Math.floor((i - 1) / 2)],]
i = Math.floor((i - 1) / 2)
}
}
// 从顶部向下调整堆结构
_sortFront() {
let i = 0
while (i * 2 + 1 < this.arr.length) {
let temp = i
if (this.arr[temp][1] > this.arr[i * 2 + 1][1]) temp = i * 2 + 1
if (i * 2 + 2 < this.arr.length && this.arr[temp][1] > this.arr[i * 2 + 2][1]) temp = i * 2 + 2
if (temp === i) break
[this.arr[i], this.arr[temp]] = [this.arr[temp], this.arr[i]]
i = temp
}
}
}