相似题目:

  1. ​【常考排序算法】堆排序​​.
  2. ​【剑指 Offer】 40. 最小的k个数​​.
  3. ​【LeetCode】215. 数组中的第K个最大元素​​.

1. 题目描述

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  1. 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  2. 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
  3. 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。你可以按任意顺序返回答案。

2. 解题思路

  1. 借助 哈希表 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率
  2. 维护一个元素数目为【LeetCode】347. 前 K 个高频元素 (纯手写)_python的最小堆(记住:求最小 top k 问题用最大堆,求最大 top k 问题用最小堆)
  3. 每次都将新的元素与堆顶元素(堆中频率最小的元素)进行比较。如果新的元素的频率比堆顶端的元素大,则弹出堆顶端的元素,将新的元素添加进堆中
  4. 最终,堆中的 k个元素即为前 k 个高频元素

具体可见下图:

【LeetCode】347. 前 K 个高频元素 (纯手写)_算法_02

3. 代码

法一、纯手写

class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
def heapify(k, i):
c1 = 2 * i + 1 # 左孩子
c2 = 2 * i + 2 # 右孩子
min = i # 假设最小值在 i 的位置
if c1 < k and hashList[c1][1] < hashList[min][1]:
min = c1
if c2 < k and hashList[c2][1] < hashList[min][1]:
min = c2
if min != i:
hashList[min], hashList[i] = hashList[i], hashList[min] # 整对 一起调换
heapify(k, min)

# 将元素和出现的次数存入哈希表
hashmap = {}
for num in nums:
if num not in hashmap:
hashmap[num] = 1
else:
hashmap[num] += 1
# 将哈希表转换为二维列表,便于按照顺序索引
hashList = [[key, value] for key, value in hashmap.items()]
# build heap 由于需要取部分数据建立堆,不方便对 arr[:k] 进行传参,所以采用嵌套函数,
# 免去多次传参的麻烦
for i in range((k-2)//2, -1, -1):
heapify(k, i)
# 依次和最小堆的堆顶比较,若比堆顶小,无需处理;如果比堆顶大,则和堆顶元素调换
n = len(hashList)
for i in range(k, n):
if hashList[0][1] < hashList[i][1]:
hashList[0] = hashList[i] # 整个单元,即[key, value] 对都调换,而不是只有值调换
heapify(k, 0)
ans = [hashList[i][0] for i in range(k)] # 前 k 个[key, value] 对 的 key
return

结果如同:

【LeetCode】347. 前 K 个高频元素 (纯手写)_leetcode_03

法二、库函数
LeetCode官方题解。但是面试的时候,估计还是手写比较好。

class Solution:
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
count = collections.Counter(nums)
return heapq.nlargest(k, count.keys(), key=count.get)

参考

  1. ​LeetCode题解 1​​.
  2. ​LeetCode题解 2​​.