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

106 阅读4分钟

问题描述:

给定一个整数数组 nums 和一个整数 k,要求返回数组中出现频率最高的 k 个元素,并按升序排列。元素之间使用逗号分隔。要求算法的时间复杂度优于 O(n log n),其中 n 是数组的大小。

问题剖析:

  • 输入是一个长度为 n 的整数数组 nums 和一个整数 k,其中 1 <= k <= n,表示我们要找出出现频率前 k 高的元素。
  • 题目要求返回的元素按升序排列,并且元素之间使用逗号分隔。
  • 时间复杂度限制要求不能直接使用排序(因为排序的复杂度是 O(n log n)),我们需要使用其他方式优化算法。

解题思路:

  1. 统计频率

    • 首先需要统计每个元素在数组中的出现频率。可以利用哈希表(例如 defaultdict(int))来实现频率统计。
  2. 桶排序思想

    • 由于我们关心的是频率前 k 高的元素,而不是排序所有元素,我们可以通过桶排序来加速过程。
    • 使用一个数组 buckets,其中每个索引 i 存储所有出现频率为 i 的元素。这样我们可以把相同频率的元素放在一起,直接按频率从高到低遍历,获取前 k 个高频元素。
    • 频率最大的元素会出现在 buckets[max_freq] 中,接着是 buckets[max_freq-1],依此类推。
  3. 选取前 k 个高频元素

    • 使用桶排序后,我们可以从频率最高的桶开始,收集元素直到收集到 k 个元素为止。
    • 最终选出的 k 个元素需要按升序排列。
  4. 返回结果

    • 将选出的元素按升序排列,并返回以逗号分隔的字符串。

代码实现:

python
复制代码
def solution(nums, k):
    from collections import defaultdict

    # 统计每个元素的频率
    frequency_map = defaultdict(int)
    for num in nums:
        frequency_map[num] += 1

    # 创建桶,索引表示频率,桶中存储具有相同频率的元素
    max_freq = max(frequency_map.values())
    buckets = [[] for _ in range(max_freq + 1)]
    for num, freq in frequency_map.items():
        buckets[freq].append(num)

    # 收集前 k 个高频元素
    result = []
    for freq in range(max_freq, 0, -1):
        for num in buckets[freq]:
            result.append(num)
            if len(result) == k:
                # 将结果转换为逗号分隔的字符串
                return ",".join(map(str, sorted(result)))

代码解析:

  1. 频率统计

    • 使用 defaultdict(int) 统计每个数字在 nums 中的出现次数。时间复杂度是 O(n)
  2. 桶排序

    • 通过 max(frequency_map.values()) 获取出现频率的最大值 max_freq,并创建一个 buckets 列表,索引表示频率。每个桶中存储的是出现频率相同的元素。
    • 时间复杂度是 O(n),因为每个元素只需要被放到对应的桶中。
  3. 收集前 k 个元素

    • 从最大频率的桶开始,收集元素直到收集到 k 个为止。由于 k 的数量小于等于 n,这一过程的时间复杂度是 O(k)
  4. 结果排序

    • 最后,收集到的元素需要按升序排列。由于结果最多只有 k 个元素,因此排序的时间复杂度是 O(k log k)
  5. 返回结果

    • 最终将收集到的元素按升序排列,并转换成逗号分隔的字符串返回。

时间复杂度分析:

  • 统计频率:遍历数组一次,时间复杂度为 O(n),其中 n 是数组的长度。
  • 桶排序:创建桶并将元素分配到桶中,时间复杂度为 O(n)
  • 收集前 k 个元素:最多需要遍历 k 个元素,时间复杂度为 O(k)
  • 结果排序:最多有 k 个元素,排序的时间复杂度为 O(k log k)

因此,整体时间复杂度为 O(n + k log k)。由于 k <= n,这个复杂度要优于 O(n log n),符合题目的要求。

刷题思路总结:

  1. 统计频率:大多数与频率相关的问题,首先需要统计每个元素的频率,这通常通过哈希表来实现。
  2. 桶排序:对于频率排序问题,尤其是当我们只关心前 k 个元素时,使用桶排序是一个高效的选择。通过将频率作为桶的索引,可以将相同频率的元素分组,避免了排序所有元素的复杂度。
  3. 优化排序:当我们需要按频率排序时,可以利用桶排序或使用堆等数据结构来优化性能。通过先统计频率并进行分组,能有效减少排序的开销。
  4. 空间复杂度:在本题中,我们使用了额外的桶来存储频率分组,空间复杂度是 O(n),其中 n 是数组的大小。

通过这道题目,不仅学会了如何高效统计频率,还掌握了利用桶排序优化频率排序的技巧,这对于处理类似问题非常有帮助。

4o