问题描述:
给定一个整数数组 nums 和一个整数 k,要求返回数组中出现频率最高的 k 个元素,并按升序排列。元素之间使用逗号分隔。要求算法的时间复杂度优于 O(n log n),其中 n 是数组的大小。
问题剖析:
- 输入是一个长度为
n的整数数组nums和一个整数k,其中1 <= k <= n,表示我们要找出出现频率前k高的元素。 - 题目要求返回的元素按升序排列,并且元素之间使用逗号分隔。
- 时间复杂度限制要求不能直接使用排序(因为排序的复杂度是
O(n log n)),我们需要使用其他方式优化算法。
解题思路:
-
统计频率:
- 首先需要统计每个元素在数组中的出现频率。可以利用哈希表(例如
defaultdict(int))来实现频率统计。
- 首先需要统计每个元素在数组中的出现频率。可以利用哈希表(例如
-
桶排序思想:
- 由于我们关心的是频率前
k高的元素,而不是排序所有元素,我们可以通过桶排序来加速过程。 - 使用一个数组
buckets,其中每个索引i存储所有出现频率为i的元素。这样我们可以把相同频率的元素放在一起,直接按频率从高到低遍历,获取前k个高频元素。 - 频率最大的元素会出现在
buckets[max_freq]中,接着是buckets[max_freq-1],依此类推。
- 由于我们关心的是频率前
-
选取前
k个高频元素:- 使用桶排序后,我们可以从频率最高的桶开始,收集元素直到收集到
k个元素为止。 - 最终选出的
k个元素需要按升序排列。
- 使用桶排序后,我们可以从频率最高的桶开始,收集元素直到收集到
-
返回结果:
- 将选出的元素按升序排列,并返回以逗号分隔的字符串。
代码实现:
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)))
代码解析:
-
频率统计:
- 使用
defaultdict(int)统计每个数字在nums中的出现次数。时间复杂度是O(n)。
- 使用
-
桶排序:
- 通过
max(frequency_map.values())获取出现频率的最大值max_freq,并创建一个buckets列表,索引表示频率。每个桶中存储的是出现频率相同的元素。 - 时间复杂度是
O(n),因为每个元素只需要被放到对应的桶中。
- 通过
-
收集前
k个元素:- 从最大频率的桶开始,收集元素直到收集到
k个为止。由于k的数量小于等于n,这一过程的时间复杂度是O(k)。
- 从最大频率的桶开始,收集元素直到收集到
-
结果排序:
- 最后,收集到的元素需要按升序排列。由于结果最多只有
k个元素,因此排序的时间复杂度是O(k log k)。
- 最后,收集到的元素需要按升序排列。由于结果最多只有
-
返回结果:
- 最终将收集到的元素按升序排列,并转换成逗号分隔的字符串返回。
时间复杂度分析:
- 统计频率:遍历数组一次,时间复杂度为
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),符合题目的要求。
刷题思路总结:
- 统计频率:大多数与频率相关的问题,首先需要统计每个元素的频率,这通常通过哈希表来实现。
- 桶排序:对于频率排序问题,尤其是当我们只关心前
k个元素时,使用桶排序是一个高效的选择。通过将频率作为桶的索引,可以将相同频率的元素分组,避免了排序所有元素的复杂度。 - 优化排序:当我们需要按频率排序时,可以利用桶排序或使用堆等数据结构来优化性能。通过先统计频率并进行分组,能有效减少排序的开销。
- 空间复杂度:在本题中,我们使用了额外的桶来存储频率分组,空间复杂度是
O(n),其中n是数组的大小。
通过这道题目,不仅学会了如何高效统计频率,还掌握了利用桶排序优化频率排序的技巧,这对于处理类似问题非常有帮助。
4o