在问 · 算法题解

347. 前 K 个高频元素

中等栈与堆哈希计数 + 大小为 k 的小根堆力扣原题 ↗

题目描述

输入一个整数数组 nums 和一个整数 k。统计每个不同的数在数组中出现了几次。

按出现次数从多到少排,返回排在前 k 位的那些数。结果中的元素顺序不限。

题目保证这 k 个数构成的集合是唯一确定的。

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

解释:1 出现 3 次,2 出现 2 次,3 只出现 1 次,取前两个。

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

解释:1 和 2 各出现 4 次,3 出现 2 次。

数据范围

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • k 在 [1, 数组中不同元素的个数] 范围内
  • 答案集合唯一。

进阶:算法的时间复杂度必须低于 O(n log n),n 为数组长度。

解题思路:哈希计数 + 大小为 k 的小根堆

统计频率后维护最小堆。

为什么正确

堆中保存频率最高的 k 个元素。

复杂度分析

时间 O(n log k),额外空间 O(n)。

易错点

注意边界条件并在回溯或弹出后恢复状态。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

import heapq
from collections import Counter
class Solution:
 def topKFrequent(self,a,k):
  h=[]
  for x,c in Counter(a).items():heapq.heappush(h,(c,x));heapq.heappop(h) if len(h)>k else None
  return [x for c,x in h]

载入编辑器,自己跑一遍 →

其他解法

堆的做法稳定 O(n log k),可以边读边维护;快速选择平均 O(n),但需要拿到全部数据后才能划分,最坏情况下是 O(n²)。

解法 2:哈希计数 + 快速选择 时间 平均 O(n),最坏 O(n²),空间 O(n)

先用哈希表统计每个数的出现次数,得到 (数字, 次数) 数组。然后像快速排序一样按次数做三路划分:取中间元素的次数为基准,把数组分成大于、等于、小于基准三段。第 k 大的位置落在哪一段,就只在哪一段继续划分。结束后前 k 个元素就是答案。

平均线性,比堆少一个 log k。但会打乱数组顺序,不适合数据流场景,基准选得不好时会退化为 O(n²)。

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        items = list(collections.Counter(nums).items())
        lo, hi, t = 0, len(items) - 1, k - 1
        while lo < hi:
            p = items[(lo + hi) // 2][1]
            lt, i, gt = lo, lo, hi
            while i <= gt:
                c = items[i][1]
                if c > p:
                    items[lt], items[i] = items[i], items[lt]
                    lt += 1
                    i += 1
                elif c < p:
                    items[gt], items[i] = items[i], items[gt]
                    gt -= 1
                else:
                    i += 1
            if t < lt:
                hi = lt - 1
            elif t > gt:
                lo = gt + 1
            else:
                break
        return [x for x, _ in items[:k]]

栈与堆 专题的其他题目

查看 栈与堆 全部题目 →