查找热点数据问题| 豆包MarsCode AI 刷题

41 阅读4分钟

问题描述

给你一个整数数组 nums 和一个整数 k,请你用一个字符串返回其中出现频率前 k 高的元素。请按升序排列。

你所设计算法的时间复杂度必须优于 O(n log n),其中 n 是数组大小。

输入

  • nums: 一个正整数数组
  • k: 一个整数

返回

返回一个包含 k 个元素的字符串,数字元素之间用逗号分隔。数字元素按升序排列,表示出现频率最高的 k 个元素。

参数限制

  • 1 <= nums[i] <= 10^4
  • 1 <= nums.length <= 10^5
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

测试样例

样例1:

输入:nums = [1, 1, 1, 2, 2, 3], k = 2
输出:"1,2"
解释:元素 1 出现了 3 次,元素 2 出现了 2 次,元素 3 出现了 1 次。因此前两个高频元素是 1 和 2。

样例2:

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

样例3:

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

题目分析

我们需要从一个整数数组 nums 中找出出现频率最高的 k 个元素,并且返回这些元素按升序排列的字符串结果。我们需要知道每个元素在数组中的出现次数。在得到每个元素的频率后,我们需要选出频率前 k 高的元素。返回的元素需要按升序排列。时间复杂度必须优于 O(n log n),即要求一个比排序更高效的解决方案。

解题思路

使用 最小堆 来获取频率最高的 k 个元素,并通过堆的特性保持堆大小为 k,从而确保我们能够在时间复杂度上优化。

代码分析

  • 统计每个元素的频率
python
 代码解读
复制代码
frequency = Counter(nums)
  • 使用堆来获取频率最高的 k 个元素,这里我们使用最小堆,因为 heapq 只支持最小堆,可以通过将频率取负来模拟最大堆
python
 代码解读
复制代码
min_heap = []
    for num, freq in frequency.items():
        heapq.heappush(min_heap, (freq, num))
        if len(min_heap) > k:
            heapq.heappop(min_heap)
  • 从堆中提取元素并按升序排列,注意:堆中的元素是 (freq, num) 的形式,我们需要按 num 排序
python
 代码解读
复制代码
result = [item[1] for item in min_heap]
result.sort()
  • 最后,将结果转换为字符串并返回
python
 代码解读
复制代码
return ','.join(map(str, result))

完整代码如下:

python
 代码解读
复制代码
from collections import Counter
import heapq

def solution(nums, k):
    frequency = Counter(nums)
    
    min_heap = []
    for num, freq in frequency.items():
        heapq.heappush(min_heap, (freq, num))
        if len(min_heap) > k:
            heapq.heappop(min_heap)
    
    result = [item[1] for item in min_heap]
    result.sort()
    
    return ','.join(map(str, result))

总结

时间复杂度:

  • 频率统计:O(n),通过 Counter 来统计元素的频率。
  • 最小堆操作:O(n log k),我们将每个元素放入堆中,而堆的大小最多为 k,每次操作的复杂度是 O(log k)。
  • 排序:O(k log k),我们需要对堆中的 k 个元素按升序排序。
  • 转换为字符串:O(k),将 k 个元素转化为字符串。

因此,整体的时间复杂度是 O(n log k + k log k) ,其中 n 是数组的长度,k 是需要返回的高频元素个数。由于 k 小于或等于 n,在最坏情况下,排序操作 O(k log k) 是比较小的,整体复杂度主要由 O(n log k) 主导。

空间复杂度:

  • 频率统计:O(n),Counter 需要存储每个元素的频率。
  • 小堆:O(k),堆中最多存储 k 个元素。
  • 结果列表:O(k),用于存储最终的结果。

因此,空间复杂度为 O(n + k)

本次使用 最小堆来获取频率最高的 k 个元素,并通过堆的特性保持堆大小为 k。相比于直接排序整个数组,使用堆优化了获取前 k 高频元素的过程,时间复杂度为 O(n log k),而不是 O(n log n)。即使 k 比较小时(例如 k 很小,n 很大),堆的操作仍然保持高效。