|
| 1 | +# 347. 前 K 个高频元素 |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/top-k-frequent-elements/ |
| 4 | + |
| 5 | +- [347. 前 K 个高频元素](#347-前-k-个高频元素) |
| 6 | + - [题目描述](#题目描述) |
| 7 | + - [方法 1:哈希表](#方法-1哈希表) |
| 8 | + - [思路](#思路) |
| 9 | + - [复杂度分析](#复杂度分析) |
| 10 | + - [代码](#代码) |
| 11 | + - [方法 2:大顶堆](#方法-2大顶堆) |
| 12 | + - [思路](#思路-1) |
| 13 | + - [复杂度分析](#复杂度分析-1) |
| 14 | + - [代码](#代码-1) |
| 15 | + |
| 16 | +## 题目描述 |
| 17 | + |
| 18 | +``` |
| 19 | +给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 |
| 20 | +
|
| 21 | + |
| 22 | +
|
| 23 | +示例 1: |
| 24 | +
|
| 25 | +输入: nums = [1,1,1,2,2,3], k = 2 |
| 26 | +输出: [1,2] |
| 27 | +示例 2: |
| 28 | +
|
| 29 | +输入: nums = [1], k = 1 |
| 30 | +输出: [1] |
| 31 | + |
| 32 | +
|
| 33 | +提示: |
| 34 | +
|
| 35 | +你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。 |
| 36 | +你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。 |
| 37 | +题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。 |
| 38 | +你可以按任意顺序返回答案。 |
| 39 | +
|
| 40 | +来源:力扣(LeetCode) |
| 41 | +链接:https://leetcode-cn.com/problems/top-k-frequent-elements |
| 42 | +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 |
| 43 | +``` |
| 44 | + |
| 45 | +## 方法 1:哈希表 |
| 46 | + |
| 47 | +### 思路 |
| 48 | + |
| 49 | +- 遍历一遍数组,用哈希表统计每个数字出现的次数。 |
| 50 | +- 按次数排序,然后输出前 k 个数字。 |
| 51 | + |
| 52 | +### 复杂度分析 |
| 53 | + |
| 54 | +- 时间复杂度:$O(mlogm)$,m 是数组中不同数字的数量,最大是 N,数组的长度,这是排序的时间。 |
| 55 | +- 空间复杂度:$O(m)$,m 是数组中不同数字的数量,哈希表的空间。 |
| 56 | + |
| 57 | +### 代码 |
| 58 | + |
| 59 | +JavaScript Code |
| 60 | + |
| 61 | +```js |
| 62 | +/** |
| 63 | + * @param {number[]} nums |
| 64 | + * @param {number} k |
| 65 | + * @return {number[]} |
| 66 | + */ |
| 67 | +var topKFrequent = function (nums, k) { |
| 68 | + // 统计出现次数 |
| 69 | + const map = {}; |
| 70 | + for (const n of nums) { |
| 71 | + n in map || (map[n] = 0); |
| 72 | + map[n]++; |
| 73 | + } |
| 74 | + // 对次数进行排序然后输出前 k 个 |
| 75 | + return Object.entries(map) |
| 76 | + .sort((a, b) => b[1] - a[1]) |
| 77 | + .slice(0, k) |
| 78 | + .map(a => a[0]); |
| 79 | +}; |
| 80 | +``` |
| 81 | + |
| 82 | +## 方法 2:大顶堆 |
| 83 | + |
| 84 | +### 思路 |
| 85 | + |
| 86 | +看到 `前 k 个` 这种描述就能想到 `堆` 了,还是先把数字出现的次数统计在哈希表中,然后入堆按次数排序,再吐出来 k 个。 |
| 87 | + |
| 88 | +### 复杂度分析 |
| 89 | + |
| 90 | +- 时间复杂度:$O(klogm)$,m 是数组中不同数字的数量,堆中有 m 个元素,移除堆顶的时间是 $logm$,重复操作了 k 次。 |
| 91 | +- 空间复杂度:$O(m)$,m 是数组中不同数字的数量,哈希表和堆的空间。 |
| 92 | + |
| 93 | +### 代码 |
| 94 | + |
| 95 | +JavaScript Code |
| 96 | + |
| 97 | +```js |
| 98 | +/** |
| 99 | + * @param {number[]} nums |
| 100 | + * @param {number} k |
| 101 | + * @return {number[]} |
| 102 | + */ |
| 103 | +var topKFrequent = function (nums, k) { |
| 104 | + // 统计出现次数 |
| 105 | + const map = {}; |
| 106 | + for (const n of nums) { |
| 107 | + n in map || (map[n] = 0); |
| 108 | + map[n]++; |
| 109 | + } |
| 110 | + // 入堆,格式是:[数字,次数],按次数排序 |
| 111 | + const maxHeap = new MaxHeap(Object.entries(map), function comparator( |
| 112 | + target, |
| 113 | + compared, |
| 114 | + ) { |
| 115 | + return target[1] < compared[1]; |
| 116 | + }); |
| 117 | + // 输出前 k 个 |
| 118 | + const res = []; |
| 119 | + while (k-- > 0) { |
| 120 | + res.push(maxHeap.pop()[0]); |
| 121 | + } |
| 122 | + return res; |
| 123 | +}; |
| 124 | + |
| 125 | +// ******************************************* |
| 126 | + |
| 127 | +class Heap { |
| 128 | + constructor(list = [], comparator) { |
| 129 | + this.list = list; |
| 130 | + |
| 131 | + if (typeof comparator != 'function') { |
| 132 | + this.comparator = function comparator(target, compared) { |
| 133 | + return target < compared; |
| 134 | + }; |
| 135 | + } else { |
| 136 | + this.comparator = comparator; |
| 137 | + } |
| 138 | + |
| 139 | + this.init(); |
| 140 | + } |
| 141 | + |
| 142 | + init() { |
| 143 | + const size = this.size(); |
| 144 | + for (let i = Math.floor(size / 2) - 1; i >= 0; i--) { |
| 145 | + this.heapify(this.list, size, i); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + insert(n) { |
| 150 | + this.list.push(n); |
| 151 | + const size = this.size(); |
| 152 | + for (let i = Math.floor(size / 2) - 1; i >= 0; i--) { |
| 153 | + this.heapify(this.list, size, i); |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + peek() { |
| 158 | + return this.list[0]; |
| 159 | + } |
| 160 | + |
| 161 | + pop() { |
| 162 | + const last = this.list.pop(); |
| 163 | + if (this.size() === 0) return last; |
| 164 | + const returnItem = this.list[0]; |
| 165 | + this.list[0] = last; |
| 166 | + this.heapify(this.list, this.size(), 0); |
| 167 | + return returnItem; |
| 168 | + } |
| 169 | + |
| 170 | + size() { |
| 171 | + return this.list.length; |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +class MaxHeap extends Heap { |
| 176 | + constructor(list, comparator) { |
| 177 | + super(list, comparator); |
| 178 | + } |
| 179 | + |
| 180 | + heapify(arr, size, i) { |
| 181 | + let largest = i; |
| 182 | + const left = Math.floor(i * 2 + 1); |
| 183 | + const right = Math.floor(i * 2 + 2); |
| 184 | + |
| 185 | + if (left < size && this.comparator(arr[largest], arr[left])) |
| 186 | + largest = left; |
| 187 | + if (right < size && this.comparator(arr[largest], arr[right])) |
| 188 | + largest = right; |
| 189 | + |
| 190 | + if (largest !== i) { |
| 191 | + [arr[largest], arr[i]] = [arr[i], arr[largest]]; |
| 192 | + this.heapify(arr, size, largest); |
| 193 | + } |
| 194 | + } |
| 195 | +} |
| 196 | +``` |
0 commit comments