给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例:
输入:nums = [1,1,1,2,2,3], k = 2
输出:[1,2]
题干很简单,就是对数组中的元素进行频次计算,找到频次最多的前k和元素。那么首先就要统计元素出现的频率,然后对其进行排序,返回前k个值。
统计频率很简单,用个map映射一下,出现一次次数加一就行了,那么如何进行排序呢?《代码随想录》中介绍了一种利用堆的方法。什么是堆呢,堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。因为小顶堆可以每次将最小的元素弹出,所以这里选择小顶堆。关于二叉树的内容在下一章会介绍到。
根据示例中[1,1,1,2,2,3],‘1’有3次,‘2’有2次,‘3’有1次,那我小顶堆长什么样子呢?
这里为元素值,而不是元素的次数,由于是小顶堆,所以这三个中3是对应次数最小的,即以1——2——3的顺序排列,因为k=2,那我在堆中再进行一次弹出,弹出了3,留下了[1,2]。
完整代码如下:
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
首先,导入堆的第三方库heapq。第一部分,计算元素频率。定义一个空的map,map_[nums[i]] = map_.get(nums[i], 0) + 1是简化了代码的过程,完整是如果nums[i]不在map中,那就将其加入map,代码map_.get(nums[i], 0);如果nums[i]在map中,那就将其次数加1.
第二部分,定义一个小顶堆,开始扫描map,利用heapq.heappush自动开始建立小顶堆,该函数的()中,第一个为顶堆表示pri_que,第二个为(freq, key)代表先根据freq来排序的。如果两个元素的频率相同,则会根据key值来决定它们在堆中的顺序。
然后,只保留前k个数,如果堆的长度大于k,就heappop弹出最小的值。
定义一个大小为k的空数组,因为堆每次弹出的是最小的,但要求的结果数组是由大到小排列的,所以我们要从数组最后开始填充for i in range(k-1, -1, -1),从k-1索引(即数组最后)到能取到的0,每次以-1的步长。 result[i] = heapq.heappop(pri_que)[1],result[i]等于每次堆中弹出的值的[1]索引,即上面(freq, key)中的key。最后完成结果数组。
那我不用这个小顶堆行么,map不是自带排序么,我直接排序一下,找到前k个值不行么?也可以,利用map的排序,完整代码如下:
from typing import List
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 统计每个元素出现的频率
map_ = {} # 元素: 出现次数
for num in nums:
map_[num] = map_.get(num, 0) + 1
# 按照出现频率排序,得到频率最高的前 k 个元素
sorted_items = sorted(map_.items(), key=lambda x: x[1], reverse=True)
# 取出前 k 个元素的键
result = [item[0] for item in sorted_items[:k]]
return result
统计元素频率是一样的。sorted_items = sorted(map_.items(), key=lambda x: x[1], reverse=True)中利用sort进行排序,定义选择排序的函数key=lambda x: x[1],以x[1]即(key,freq)中的freq为对象进行排序。
result = [item[0] for item in sorted_items[:k]]代码是简化后的,但也就是定义空数组,然后将排序后数组中的[:k]部分取出来的item中的第一个数item[0](即key)取出来填入result。简化的代码写不出来就安安心心的拆分开来写完整一点。
从代码简洁度和理解来讲,map直接排序好像更简单一点,那这两种方法的区别在哪里呢?在于k的大小,对于小k的情况,小顶堆方法可能更高效,而对于较大k,排序方法可能更简单和直接。
标签:map,排序,nums,队列,元素,key,小顶,高频 From: https://blog.csdn.net/plutomty/article/details/140903036