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

130 阅读4分钟

查找热点数据问题

问题描述

给定一个整数数组 nums 和一个整数 k,要求返回出现频率前 k 高的元素,按升序排列。

  • 1 <= nums.length <= 10^5
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 要求算法的时间复杂度优于 O(n log n),其中 n 是数组大小。

示例:

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

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

解题思路

题目要求我们在 优于 O(n log n) 的时间复杂度内找到出现频率前 k 高的元素。这提示我们不能对整个数组进行排序,需要寻找线性时间复杂度的算法。

主要思路:

  1. 统计频率: 使用哈希表(如 dictdefaultdict)统计每个元素出现的次数。

  2. 桶排序: 我们可以将元素按照出现频率进行分组,使用一个数组(桶)存储频率相同的元素。

  3. 收集结果: 从高到低遍历频率,依次收集元素,直到收集到 k 个元素为止。

  4. 排序输出: 最后对收集的元素进行排序,按升序返回。

算法步骤

  1. 统计每个元素的频率:

    • 使用哈希表遍历 nums,统计每个元素出现的次数。
    • 例如,对于 nums = [1, 1, 1, 2, 2, 3],统计结果为 {1: 3, 2: 2, 3: 1}
  2. 构建桶:

    • 创建一个列表 buckets,其中第 i 个桶存储出现频率为 i 的元素。
    • 桶的数量为 n + 1,因为元素的最大频率不会超过数组的长度 n
    • 将哈希表中的元素根据频率放入对应的桶中。
  3. 从高频到低频收集元素:

    • 从频率最高的桶开始(即从 n1),依次遍历。
    • 在每个桶中,可能存在多个元素,先对桶内的元素进行排序(因为同频率的元素需要按升序排列)。
    • 将元素添加到结果列表 res 中,直到收集到 k 个元素。
  4. 返回结果:

    • 对结果列表 res 进行排序,确保最终输出按升序排列。
    • 返回结果列表 res

代码实现

def solution(nums, k):
    from collections import defaultdict
    # 1. 统计频率
    freq_dict = defaultdict(int)
    for num in nums:
        freq_dict[num] += 1

    # 2. 构建桶
    n = len(nums)
    buckets = [[] for _ in range(n + 1)]
    for num, freq in freq_dict.items():
        buckets[freq].append(num)

    # 3. 从高频到低频收集元素
    res = []
    for freq in range(n, 0, -1):
        if buckets[freq]:
            buckets[freq].sort()  # 对桶内元素排序
            res.extend(buckets[freq])
            if len(res) >= k:
                break

    # 4. 返回结果
    res = res[:k]
    res.sort()
    return res

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

算法复杂度分析

  • 时间复杂度:

    • 统计频率: 遍历一次数组,时间复杂度为 O(n)
    • 构建桶: 遍历频率字典,时间复杂度为 O(m),其中 m 是不同元素的数量,m <= n
    • 收集结果: 最坏情况下遍历所有频率,时间复杂度为 O(n)
    • 排序桶内元素和结果列表: 由于桶内元素数量通常较少,排序时间可以视为常数。此外,最终结果列表的长度不超过 k,对其排序的时间复杂度为 O(k log k)

    总体时间复杂度为 O(n),满足优于 O(n log n) 的要求。

  • 空间复杂度:

    • 需要额外的空间来存储频率字典和桶,空间复杂度为 O(n)

注意事项

  • 同频率元素的处理: 当多个元素具有相同的出现频率时,需要按元素值的升序排列。因此,在将元素添加到结果列表之前,需要对桶内的元素进行排序。

  • 结果列表的长度: 需要注意在收集元素时,可能会超过 k 个元素,因此在最后返回结果时,需截取前 k 个元素。

  • 边界情况:nums 只有一个元素时,算法也能正常处理。

总结

通过使用哈希表统计频率和桶排序的方法,我们成功地在线性时间内解决了该问题。该方法巧妙地利用了频率信息,将问题从对元素排序转换为对频率排序,满足了时间复杂度的要求。

学习收获

  • 哈希表的应用: 统计元素频率时,哈希表是非常高效的工具。

  • 桶排序思想: 当元素的范围有限或频率信息可用时,桶排序可以在线性时间内完成排序任务。

  • 算法优化: 在时间复杂度要求较高的情况下,尝试从数据特征出发,寻找特殊的算法或数据结构来优化性能。

  • 细节处理: 在实现过程中,注意对同频率元素的排序以及结果列表的截取,确保输出符合题目要求。