Orion's Studio.

算法(45)- 前K个高频元素

2024/03/19

题目(medium)

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

思路

优先级队列,就是一个披着队列外衣的堆。

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。

所以我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// js 没有堆 需要自己构造
class Heap {
constructor(compareFn) {
this.compareFn = compareFn;
this.queue = [];
}

// 添加
push(item) {
// 推入元素
this.queue.push(item);

// 上浮
let index = this.size() - 1; // 记录推入元素下标
let parent = Math.floor((index - 1) / 2); // 记录父节点下标

while (parent >= 0 && this.compare(parent, index) > 0) { // 注意compare参数顺序
[this.queue[index], this.queue[parent]] = [this.queue[parent], this.queue[index]];

// 更新下标
index = parent;
parent = Math.floor((index - 1) / 2);
}
}

// 获取堆顶元素并移除
pop() {
// 堆顶元素
const out = this.queue[0];

// 移除堆顶元素 填入最后一个元素
this.queue[0] = this.queue.pop();

// 下沉
let index = 0; // 记录下沉元素下标
let left = 1; // left 是左子节点下标 left + 1 则是右子节点下标
let searchChild = this.compare(left, left + 1) > 0 ? left + 1 : left;

while (searchChild !== undefined && this.compare(index, searchChild) > 0) { // 注意compare参数顺序
[this.queue[index], this.queue[searchChild]] = [this.queue[searchChild], this.queue[index]];

// 更新下标
index = searchChild;
left = 2 * index + 1;
searchChild = this.compare(left, left + 1) > 0 ? left + 1 : left;
}

return out;
}

size() {
return this.queue.length;
}

// 使用传入的 compareFn 比较两个位置的元素
compare(index1, index2) {
// 处理下标越界问题
if (this.queue[index1] === undefined) return 1;
if (this.queue[index2] === undefined) return -1;

return this.compareFn(this.queue[index1], this.queue[index2]);
}

}

const topKFrequent = function (nums, k) {
const map = new Map();

for (const num of nums) {
map.set(num, (map.get(num) || 0) + 1);
}

// 创建小顶堆
const heap= new Heap((a, b) => a[1] - b[1]);

// entry 是一个长度为2的数组,0位置存储key,1位置存储value
for (const entry of map.entries()) {
heap.push(entry);

if (heap.size() > k) {
heap.pop();
}
}

// return heap.queue.map(e => e[0]);

const res = [];

for (let i = heap.size() - 1; i >= 0; i--) {
res[i] = heap.pop()[0];
}

return res;
};

时间复杂度:O(nlogk)。
空间复杂度:O(n)。

CATALOG
  1. 1. 题目(medium)
  2. 2. 思路