题目分析
题目要求:从一个数组中找出出现频率最高的前 k 个元素,并按升序输出。需要满足以下条件:
- 输出为一个字符串,数字之间用逗号分隔。
- 算法时间复杂度必须优于
O(n log n)。
难点分析:
- 时间复杂度限制:传统排序方式是
O(n log n),本题要求比排序更高效,需使用堆或哈希表等数据结构。 - 频率统计:需要快速统计每个元素的出现次数。
- 结果输出格式化:按频率前
k高、升序排列,返回字符串格式。
解题思路
-
统计频率使用 Python 的
collections.Counter对数组进行统计,得到每个元素的频率。 -
使用堆优化
- 利用小顶堆(最小堆)保持频率最高的
k个元素。 - 堆中存储
(频率, 元素)的元组,优先依据频率排序。 - 遍历时,当堆的大小超过
k,移除堆顶(频率最小的元素)。
- 利用小顶堆(最小堆)保持频率最高的
-
结果处理
- 从堆中提取元素时,由于堆不保证顺序,需要额外对元素进行升序排列。
- 将排序后的结果转为字符串输出。
时间复杂度分析:
- 频率统计:
O(n),其中n是数组长度。 - 堆操作:每次插入或删除堆的复杂度为
O(log k),最多遍历n次,总复杂度为O(n log k)。 - 结果排序:提取堆中
k个元素后排序,复杂度为O(k log k)。总体复杂度:O(n log k)。
代码详解
以下是代码与详解:
python
复制代码
import heapq
from collections import Counter
def solution(nums, k):
"""
查找出现频率最高的前 k 个元素,并返回按升序排列的字符串
:param nums: List[int] 输入数组
:param k: int 需要返回的高频元素个数
:return: str 按升序排列的高频元素字符串
"""
# 1. 统计每个元素的频率
freq_map = Counter(nums)
# freq_map 示例:{1: 3, 2: 2, 3: 1}
# 2. 使用最小堆保存频率最高的 k 个元素
heap = [] # 小顶堆,存储 (频率, 元素) 元组
for num, freq in freq_map.items():
heapq.heappush(heap, (freq, num)) # 插入堆
# 如果堆的大小超过 k,则弹出频率最低的元素
if len(heap) > k:
heapq.heappop(heap)
# 3. 提取堆中元素并排序
top_k = [num for freq, num in heap] # 仅保留元素部分
top_k.sort() # 按升序排列
# 4. 返回结果
return ",".join(map(str, top_k)) # 拼接成字符串
测试结果
测试用例:
python
复制代码
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"
执行结果:
- 用例 1:元素
1出现 3 次,2出现 2 次,答案是"1,2"。 - 用例 2:数组中只有一个元素,直接返回
"1"。 - 用例 3:
4和2出现 3 次,按升序返回"2,4"。
个人感悟
- 堆的灵活应用本题中的小顶堆操作是一个亮点,它既能限制空间复杂度(仅存储
k个元素),又保证了性能。学习这种思路后,我对堆的实际应用场景有了更深的理解,特别是在处理“前 K 大”或“前 K 小”问题时,这是一种高效的通用方法。 - 格式化输出的细节除了解决核心问题,本题还考察了结果的处理和格式化能力(如升序排序和字符串拼接)。在实际项目中,这种“结果精加工”的能力同样重要。
- 算法优化意识本题明确要求时间复杂度优于
O(n log n),提醒我在解题时要特别注意复杂度分析。这种限制对培养算法优化意识非常有帮助。 - 通用性本题方法不仅能解决“前 K 高频元素”的问题,也能扩展到其他场景,比如找到频率最高的关键词、统计流量高的页面等。这种“抽象后应用”的能力在实际开发中非常实用。
下一步学习计划:我计划进一步学习和应用堆、