这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
前文我们讲了# 堆、堆排序的javascript基础篇,这篇文章我们来实际运用一下~
前 K 个高频元素
题目例1的意思是给定的数组中有3个1、2个2、1个3,要返回出现频率前2高的元素,所以是1和2。
分析: 其实我们首先应该想到的是可以直接遍历数组然后生成一个含有每个元素出现次数的新数组,再对他进行排序,取前K位元素就行了。。该算法的时间复杂度,第一次遍历时间复杂度为O(N),后面对生成数组排序,根据算法不同和最好最坏情况不同,时间复杂度在O(K)到O(K2)不等,平均时间复杂度为O(N)。不过我们本章要学习的是堆排序,所以我们就不用这种方案。
堆排序的方案,我们需要出现次数前K的数据,所以我们要建立一个长度为K的小顶堆,堆内的数据都要大于堆外的数据。这样遍历堆就能拿到出现频率前K高的元素了。
-
首先遍历输入数组,放入map中,map中key是元素值,value是元素出现的次数。
-
遍历map,构建长度为K的小顶堆(这里要注意,我们最后求的值是元素值,所以堆内每个节点要包含元素值和出现的次数,出现的次数是我们比较大小的关键)。
-
堆内所有节点即是本题结果。
我们依照例子画个图,大家就懂了~
nums = [1, 1, 1, 2, 2, 3], k = 2
Map(3) { 1 => 3, 2 => 2, 3 => 1 }
第一次,堆内塞入出现次数3
第二次,堆内塞入出现次数2,堆化
第三次,因为堆满了,出现次数1比堆顶小,所以入不了堆
结果就是出现2次的2和出现3次的1,result = [2, 1]
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
class HeapData {
constructor(key, value) {
this.value = value
this.ret = key
}
}
var topKFrequent = function (nums, k) {
// K长度的小顶堆
const heap = [null];
// 堆内数量
let count = 0;
const map = new Map();
for (let val of nums) {
const data = map.get(val) ? (map.get(val) + 1) : 1;
map.set(val, data)
}
for (let [key, value] of map) {
const data = new HeapData(key, value);
if (count < k) {
heap.push(data);
count++;
heapifyUp();
} else if (data.value > heap[1].value) {
heap[1] = data;
heapifyDown(1)
}
}
// 自下而上堆化
function heapifyUp() {
let i = count;
while ((i >> 1) > 0 && heap[i].value < heap[i >> 1].value) {
swap(heap, i, i >> 1);
i = i >> 1;
}
}
function heapifyDown(i) {
while (true) {
let pos = i;
if (i * 2 <= count && heap[i * 2].value < heap[i].value) pos = i * 2;
if (i * 2 + 1 <= count && heap[i * 2 + 1].value < heap[pos].value) pos = i * 2 + 1;
if (i === pos) break;
swap(heap, i, pos);
i = pos;
}
}
const result = [];
while (heap.length > 1) {
result.push(heap.pop().ret)
}
return result;
};
function swap(arr, a, b) {
[arr[a], arr[b]] = [arr[b], arr[a]];
}
😀😀😀欢迎大家讨论,看完记得点个👍🏻哦。本文的代码已放Github,里面还有之前算法的代码,后续代码也会陆续更新,欢迎大家不吝赐教。