问题描述:
给你一个整数数组 nums 和一个整数 k,请你用一个字符串返回其中出现频率前 k 高的元素。请按升序排列。
你所设计算法的时间复杂度必须优于 O(n log n),其中 n 是数组大小。
思路解析:
解决方案步骤
-
统计频率:
使用
collections.Counter来统计数组nums中每个元素的出现频率。这一步的时间复杂度是 O(n),其中 n 是数组
nums的长度。 -
构建小顶堆:
使用 Python 的
heapq模块来构建一个大小为 k 的小顶堆。堆中的元素是包含元素值和频率的元组,但堆是根据频率来排序的(频率作为元组的第一个元素)。
遍历频率统计结果,对于每个元素和它的频率:
a. 如果堆的大小小于 k,直接将元素(频率,数值)添加到堆中。
b. 如果堆的大小等于 k,并且当前元素的频率大于堆顶元素的频率,则弹出堆顶元素,并将当前元素添加到堆中。
这一步的时间复杂度是 O(nlogk),因为每次插入和删除操作的时间复杂度是 O(logk),并且我们最多对 n 个元素执行这些操作。
-
提取堆中元素:
从堆中提取所有元素,只保留数值部分。
这一步的时间复杂度是 O(k),因为我只是遍历堆中的 k 个元素。
-
排序:
对提取出的数值进行排序,以满足题目要求的按数值升序排列。
这一步的时间复杂度是 O(klogk),因为排序算法(如快速排序、归并排序或堆排序)的时间复杂度通常是 O(mlogm),其中 m 是要排序的元素数量,在这里 m=k。
-
构建结果字符串:
将排序后的数值转换为字符串,并用逗号分隔。
这一步的时间复杂度是 O(k),因为字符串连接和格式化操作的时间复杂度与元素数量成线性关系。
代码实现:
from collections import Counter
def solution(nums, k):
p = Counter(nums)
most_elements = p.most_common(k)
sort_elements = sorted([num for num, count in most_elements])
result = ','.join(map(str, sort_elements))
return result
个人思考:
- 虽然排序步骤的时间复杂度是 O(klogk),但在大多数情况下,由于 k≪n(即 k 远小于 n),整个算法的总时间复杂度仍然优于 O(nlogn)。
- 如果 k 的值非常大,以至于排序步骤成为性能瓶颈,那么可能需要考虑使用其他数据结构或算法来优化这一步,比如使用平衡二叉搜索树(如 AVL 树或红黑树)来同时维护频率和数值的排序。然而,这些数据结构在插入和删除操作上的时间复杂度通常也是 O(logm),并且在实现上可能更加复杂。