青训营X豆包MarsCode 技术训练营题目解析——查找热点数据问题| 豆包MarsCode AI 刷题

144 阅读3分钟

要解决这个问题,我们需要统计每个元素的出现频率,然后找到频率最高的 kk 个元素,并按升序排列。要求算法的时间复杂度必须优于 O(nlog⁡n)O(n \log n),因此我们可以使用 桶排序 来优化频率统计和排序过程。


算法思路

  1. 统计频率

    • 使用哈希表(Python 的 collections.Counter)来统计每个数字的出现频率。
    • 时间复杂度:O(n)O(n)。
  2. 寻找前 kk 个高频元素

    • 通过最小堆维护前 kk 个频率最高的元素。
    • 时间复杂度:O(nlog⁡k)O(n \log k)。
  3. 排序结果

    • 将前 kk 个元素按升序排序。
    • 时间复杂度:O(klog⁡k)O(k \log k)。
  4. 返回结果

    • 将结果转化为以逗号分隔的字符串格式。

代码实现

以下是基于最小堆的实现:

from collections import Counter
import heapq

def topKFrequent(nums, k):
    # 1. 统计每个数字的出现频率
    count = Counter(nums)
    
    # 2. 使用最小堆维护前 k 个频率最高的元素
    heap = []
    for num, freq in count.items():
        heapq.heappush(heap, (freq, num))  # 将 (频率, 元素) 入堆
        if len(heap) > k:
            heapq.heappop(heap)  # 堆的大小保持为 k
    
    # 3. 提取堆中的元素并按升序排列
    top_k = sorted([num for freq, num in heap])
    
    # 4. 转化为逗号分隔的字符串
    return ",".join(map(str, top_k))

# 测试用例
if __name__ == "__main__":
    print(topKFrequent([1, 1, 1, 2, 2, 3], 2))  # 输出: "1,2"
    print(topKFrequent([1], 1))  # 输出: "1"
    print(topKFrequent([4, 4, 4, 2, 2, 2, 3, 3, 1], 2))  # 输出: "2,4"

代码详解

1. 使用 Counter 统计频率

collections.Counter 是 Python 内置的哈希表工具,用于统计元素出现的次数:

count = Counter(nums)

对于输入 nums = [1, 1, 1, 2, 2, 3],得到的 count 为:

{1: 3, 2: 2, 3: 1}

2. 使用最小堆维护前 kk 个高频元素

heapq 是 Python 的优先队列工具,默认是最小堆。通过频率进行堆排序:

  • 将每个元素 (频率, 值) 推入堆中。
  • 如果堆的大小超过 kk,弹出堆顶最小元素。

例如,对于 nums = [1, 1, 1, 2, 2, 3]k = 2,堆的变化过程如下:

  1. 插入 (3, 1),堆为 [(3, 1)]
  2. 插入 (2, 2),堆为 [(2, 2), (3, 1)]
  3. 插入 (1, 3),堆为 [(1, 3), (3, 1), (2, 2)]
  4. 弹出最小频率,堆为 [(2, 2), (3, 1)]

最终,堆中保留的是频率前 kk 高的元素。

3. 按升序排列

将堆中的元素按值排序:

top_k = sorted([num for freq, num in heap])

对于 heap = [(2, 2), (3, 1)],提取元素并排序后得到 [1, 2]

4. 转化为字符串

使用 ",".join(map(str, top_k)) 将结果转换为以逗号分隔的字符串。


复杂度分析

  1. 时间复杂度

    • 统计频率:O(n)O(n)
    • 堆操作:O(nlog⁡k)O(n \log k),其中 kk 是堆的大小
    • 排序:O(klog⁡k)O(k \log k)
    • 总复杂度:O(nlog⁡k+klog⁡k)O(n \log k + k \log k)。对于 k≪nk \ll n,时间复杂度接近 O(nlog⁡k)O(n \log k)。
  2. 空间复杂度

    • 频率计数表:O(u)O(u),其中 uu 是数组中不同元素的数量。
    • 堆:O(k)O(k)。
    • 总空间复杂度:O(u+k)O(u + k)。

测试结果

测试样例 1

nums = [1, 1, 1, 2, 2, 3]
k = 2

输出:

"1,2"

测试样例 2

nums = [1]
k = 1

输出:

"1"

测试样例 3

nums = [4, 4, 4, 2, 2, 2, 3, 3, 1]
k = 2

输出:

"2,4"

总结

通过使用哈希表统计频率和最小堆维护前 kk 个高频元素,我们成功将时间复杂度优化为 O(nlog⁡k)O(n \log k),满足题目对效率的要求。