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

61 阅读4分钟

题目解析

本题的目标是从一个整数数组中找到出现频率前 k 高的元素,并按升序排列返回。题目要求算法的时间复杂度必须优于 O(n log n),其中 n 是数组大小。这意味着我们不能简单地使用排序方法,因为排序的时间复杂度是 O(n log n),而需要考虑一种更高效的方法来解决此问题。

思路

  1. 计数频率:首先,我们需要统计数组中每个数字的出现频率。Python 的 collections.Counter 类是实现这一功能的一个方便工具,它可以迅速计算出每个元素的频率。

  2. 选择前 k 个高频元素:为了找到频率最高的 k 个元素,我们可以使用最小堆(Min-Heap)。最小堆能够有效地保持堆顶始终为堆中最小的元素,因此可以通过堆的特性方便地移除频率最低的元素。我们可以将元素的频率作为堆的关键字,并通过负数来模拟大根堆,从而确保堆中的元素按频率从大到小排列。

  3. 排序与返回结果:一旦我们从堆中提取出前 k 个高频元素,接下来只需按元素的值升序排列,然后返回格式化的字符串。

代码详解

import heapq
from collections import Counter

def solution(nums, k):
    # 统计每个元素出现的频率
    count = Counter(nums)
    
    # 使用最小堆,堆的大小为 k,存储的是 (频率, 元素) 元组
    # heapq 默认是小根堆,我们需要频率高的排在前面,所以需要用负数来实现大根堆的效果
    heap = []
    
    for num, freq in count.items():
        heapq.heappush(heap, (freq, num))
        if len(heap) > k:  # 保证堆的大小始终为 k
            heapq.heappop(heap)
    
    # 从堆中提取元素,并按升序排序
    result = [num for freq, num in heap]
    result.sort()  # 按照元素值升序排列
    return ",".join(map(str, result))
  1. Counter(nums):这一步通过 Counter 类计算出每个数字在数组中的频率,返回一个字典,键是数组中的元素,值是该元素的出现次数。

  2. 最小堆操作

    • heapq.heappush(heap, (freq, num)) 将每个数字的频率和该数字本身组成一个元组 (freq, num) 并推入堆中。
    • 如果堆的大小超过了 k,那么我们通过 heapq.heappop(heap) 弹出堆顶元素,保证堆的大小始终为 k。
  3. 提取并排序

    • 使用列表推导 [num for freq, num in heap] 从堆中提取所有的元素。
    • 对提取出的元素按大小升序排列。
  4. 返回结果:将结果转换为以逗号分隔的字符串形式并返回。

知识总结

  1. Counter 的使用Counter 是 Python 中的一个高效工具,常用于统计元素的频率。通过 Counter(nums) 可以快速计算出一个字典,其中键是数组中的元素,值是该元素的出现次数。它在本题中的作用非常明显,能简化频率统计的过程。

  2. 堆的使用:堆(特别是最小堆)在选择前 k 个元素的过程中非常有用。通过将频率较高的元素存入堆并保持堆的大小不超过 k,可以有效地找到频率前 k 的元素。使用堆可以将时间复杂度降到 O(n log k),大大优化了排序方法的性能。

  3. 时间复杂度分析

    • 频率统计:通过 Counter 统计频率的时间复杂度为 O(n),其中 n 是数组的长度。
    • 堆操作:对于每个元素,我们将其插入堆中,最小堆操作的时间复杂度为 O(log k),总共会进行 n 次插入操作,因此堆的操作复杂度为 O(n log k)。
    • 排序操作:最终的排序操作是对 k 个元素进行排序,时间复杂度为 O(k log k)。

    因此,总的时间复杂度为 O(n log k),符合题目要求的优于 O(n log n) 的条件。

  4. 重点复习数据结构:在解决本题的过程中,堆和哈希表(Counter)是两个非常关键的工具。建议深入学习堆的操作原理及应用,尤其是最小堆和最大堆的转换(通过负数实现大根堆)。同时要熟悉 Python 中的 collections.Counter,这是一个非常高效且常用的工具,能够帮助我们快速处理频率统计问题。

工具运用

豆包MarsCode AI 提供的自动化刷题功能非常方便,可以根据自己的掌握程度进行选择性练习。例如,在本题中,通过自动化的测试与解析,我能够快速理解堆和 Counter 的用法及其应用场景。此外,错题的反馈和详细解析帮助我避免重复错误,进而提升自己的算法水平。