Leetcode原题链接:前K个高频元素
一、思路
- 统计每个数字出现次数,存入字典。
- 维护以次数为准的大小为
k
的小顶堆,每次push
新数后,因为要维护小顶堆大小为k
,因此pop
出堆顶元素(最小)。
二、代码
1、小顶堆思想代码
(没写出来,看看高手代码)
#时间复杂度:O(nlogk)
#空间复杂度:O(n)
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
#要统计元素出现频率
map_ = {} #nums[i]:对应出现的次数
for i in range(len(nums)):
map_[nums[i]] = map_.get(nums[i], 0) + 1
#对频率排序
#定义一个小顶堆,大小为k
pri_que = [] #小顶堆
#用固定大小为k的小顶堆,扫描所有频率的数值
for key, freq in map_.items():
heapq.heappush(pri_que, (freq, key))
if len(pri_que) > k: #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
heapq.heappop(pri_que)
#找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组
result = [0] * k
for i in range(k-1, -1, -1):
result[i] = heapq.heappop(pri_que)[1]
return result
其实最后没要求从高到低排序,所以倒也不用倒序。
2、另一个看起来比较简单的代码
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 先计次
n = len(nums)
dic = {}
for i in range(n):
if nums[i] not in dic:
dic[nums[i]] = 1
else:
dic[nums[i]] += 1
# 每次弹出一个最大,弹k次
res = []
while k > 0:
tmp = 0
for num in dic:
if dic[num] > tmp:
tmp = dic[num]
cur = num
dic[cur] = -1
res.append(cur)
k -= 1
return res
这段代码的时间复杂度为O(klogn),满足题意。
其中,计次的时间复杂度为O(n),弹出最大的元素的时间复杂度为O(logn)。因为这个操作被执行了k次,所以总时间复杂度为O(klogn)。
三、总结
- 没用过
heapq
库,这里做个简单介绍:
heapq
是Python的一个内置库,它提供了堆队列算法的实现。堆是一种特殊的数据结构,它是一棵树,其中每个父节点的值都小于或等于其子节点的值。在Python中,堆通常使用列表来实现。
heapq
库提供了许多有用的函数,包括:
heappush
:将一个元素推入堆中。
heappop
:从堆中弹出最小的元素。
heapify
:将列表转换为堆。
heapreplace
:弹出最小的元素并将新元素推入堆中。
以下是一个使用heapq库的示例:
import heapq
my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
heapq.heapify(my_list)
print(my_list) # 输出 [1, 1, 2, 3, 3, 9, 4, 6, 5, 5, 5]
这个例子将一个列表转换为堆,并打印出结果。
- 方法2把k逐次减小,每次弹出最大值,非常直观,之前一直觉得题目给的参数不能修改,也算是思想被禁锢了。
部分内容参考代码随想录