查找热点数据问题
问题描述
给定一个整数数组 nums 和一个整数 k,要求返回出现频率前 k 高的元素,按升序排列。
1 <= nums.length <= 10^5k的取值范围是[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 高的元素。这提示我们不能对整个数组进行排序,需要寻找线性时间复杂度的算法。
主要思路:
-
统计频率: 使用哈希表(如
dict或defaultdict)统计每个元素出现的次数。 -
桶排序: 我们可以将元素按照出现频率进行分组,使用一个数组(桶)存储频率相同的元素。
-
收集结果: 从高到低遍历频率,依次收集元素,直到收集到
k个元素为止。 -
排序输出: 最后对收集的元素进行排序,按升序返回。
算法步骤
-
统计每个元素的频率:
- 使用哈希表遍历
nums,统计每个元素出现的次数。 - 例如,对于
nums = [1, 1, 1, 2, 2, 3],统计结果为{1: 3, 2: 2, 3: 1}。
- 使用哈希表遍历
-
构建桶:
- 创建一个列表
buckets,其中第i个桶存储出现频率为i的元素。 - 桶的数量为
n + 1,因为元素的最大频率不会超过数组的长度n。 - 将哈希表中的元素根据频率放入对应的桶中。
- 创建一个列表
-
从高频到低频收集元素:
- 从频率最高的桶开始(即从
n到1),依次遍历。 - 在每个桶中,可能存在多个元素,先对桶内的元素进行排序(因为同频率的元素需要按升序排列)。
- 将元素添加到结果列表
res中,直到收集到k个元素。
- 从频率最高的桶开始(即从
-
返回结果:
- 对结果列表
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只有一个元素时,算法也能正常处理。
总结
通过使用哈希表统计频率和桶排序的方法,我们成功地在线性时间内解决了该问题。该方法巧妙地利用了频率信息,将问题从对元素排序转换为对频率排序,满足了时间复杂度的要求。
学习收获
-
哈希表的应用: 统计元素频率时,哈希表是非常高效的工具。
-
桶排序思想: 当元素的范围有限或频率信息可用时,桶排序可以在线性时间内完成排序任务。
-
算法优化: 在时间复杂度要求较高的情况下,尝试从数据特征出发,寻找特殊的算法或数据结构来优化性能。
-
细节处理: 在实现过程中,注意对同频率元素的排序以及结果列表的截取,确保输出符合题目要求。