给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:输入: nums = [1], k = 1
输出: [1]提示:
1 <= nums.length <= 1 0 5 10^5 105
k 的取值范围是 [1, 数组中不相同的元素的个数]
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
import collections
import heapq
class Solution:
def topKFrequent(self, nums: list, k: int) -> list:
tmp = {}
for n in nums:
if n in tmp:
tmp[n] += 1
else:
tmp[n] = 1
tmp = sorted(tmp.items(), key=lambda x: x[1], reverse=True)
return [tmp[i][0] for i in range(k)]
#使用小顶堆,重点解法
def topKFrequent(self, nums: list, k: int) -> list:
freqs = collections.Counter(nums)
heap = []
for key, val in freqs.items():
heapq.heappush(heap, [val, key])
if len(heap) > k:
heapq.heappop(heap)
return [v for k, v in heap]
if __name__ == '__main__':
s = Solution()
print(s.topKFrequent([1,1,1,2,2,3], 2))
print(s.topKFrequent([1], 1))
首先遍历整个数组,并使用哈希表记录每个数字出现的次数,并形成一个「出现次数数组」。找出原数组的前 k 个高频元素,就相当于找出「出现次数数组」的前 k 大的值。
最简单的做法是给「出现次数数组」排序。但由于可能有 O ( N ) O(N) O(N) 个不同的出现次数(其中 N 为原数组长度),故总的算法复杂度会达到 O ( N log N ) O(N\log N) O(NlogN),不满足题目的要求。
在这里,我们可以利用堆的思想:建立一个小顶堆,然后遍历「出现次数数组」:
- 如果堆的元素个数小于 k,就可以直接插入堆中。
- 如果堆的元素个数等于 k,则检查堆顶与当前出现次数的大小。如果堆顶更大,说明至少有 k 个数字的出现次数比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中。
遍历完成后,堆中的元素就代表了「出现次数数组」中前 k 大的值。
复杂度分析
时间复杂度:
O
(
N
log
k
)
O(N\log k)
O(Nlogk),其中 N 为数组的长度。我们首先遍历原数组,并使用哈希表记录出现次数,每个元素需要
O
(
1
)
O(1)
O(1) 的时间,共需
O
(
N
)
O(N)
O(N) 的时间。随后,我们遍历「出现次数数组」,由于堆的大小至多为 k,因此每次堆操作需要
O
(
log
k
)
O(\log k)
O(logk) 的时间,共需
O
(
N
log
k
)
O(N\log k)
O(Nlogk) 的时间。二者之和为
O
(
N
log
k
)
O(N\log k)
O(Nlogk)。
空间复杂度:
O
(
N
)
O(N)
O(N)。哈希表的大小为
O
(
N
)
O(N)
O(N),而堆的大小为
O
(
k
)
O(k)
O(k),共计为
O
(
N
)
O(N)
O(N)。
参考
https://leetcode.cn/problems/top-k-frequent-elements/solution/qian-k-ge-gao-pin-yuan-su-by-leetcode-solution/
文章讲述了如何使用Python的heapq库和小顶堆数据结构在O(Nlogk)时间复杂度内解决LeetCode上的TopKFrequentElements问题,通过统计每个元素出现的频率并维护一个大小为k的堆来找到前k个高频元素。

被折叠的 条评论
为什么被折叠?



