问题描述
给你一个整数数组 nums 和一个整数 k,请你用一个字符串返回其中出现频率前 k 高的元素。请按升序排列。
你所设计算法的时间复杂度必须优于 O(n log n),其中 n 是数组大小。
输入 nums: 一个正整数数组 k: 一个整数 返回 返回一个包含 k 个元素的字符串,数字元素之间用逗号分隔。数字元素按升序排列,表示出现频率最高的 k 个元素。
参数限制 1 <= nums[i] <= 10^4 1 <= nums.length <= 10^5 k 的取值范围是 [1, 数组中不相同的元素的个数] 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的 测试样例 样例1:
输入:nums = [1, 1, 1, 2, 2, 3], k = 2 输出:"1,2" 解释:元素 1 出现了 3 次,元素 2 出现了 2 次,元素 3 出现了 1 次。因此前两个高频元素是 1 和 2。
样例2:
输入:nums = [1], k = 1 输出:"1"
样例3:
输入:nums = [4, 4, 4, 2, 2, 2, 3, 3, 1], k = 2 输出:"2,4"
def solution(nums, k):
# Please write your code here
return -2
if __name__ == "__main__":
# You can add more test cases here
print(solution( [1,1,1,2,2,3], 2) == "1,2" )
print(solution( [1], 1) == "1" )
要解决这个问题,需要找到频率最高的 ( k ) 个元素,并按升序排序,最后以字符串格式返回结果。
由于题目要求时间复杂度必须优于 ( O(n \log n) ),我们需要利用线性时间复杂度的方法,例如使用 堆 或 桶排序。
解题思路
-
统计频率
使用哈希表(字典)统计每个元素的出现次数。这一步的时间复杂度是 ( O(n) )。 -
选择前 ( k ) 个高频元素
- 可以使用 最小堆 来高效找到频率前 ( k ) 高的元素。
- 堆的插入和弹出操作的时间复杂度是 ( O(\log k) )。因此总时间复杂度是 ( O(n \log k) )。
-
结果处理
- 将前 ( k ) 个高频元素按升序排列,时间复杂度是 ( O(k \log k) )。
- 最后将排序后的结果转换为字符串,按要求格式返回。
代码实现
from collections import Counter
import heapq
def solution(nums, k):
# 统计每个数字的频率
freq = Counter(nums)
# 使用最小堆找到频率前 k 高的元素
min_heap = []
for num, count in freq.items():
heapq.heappush(min_heap, (count, num))
if len(min_heap) > k:
heapq.heappop(min_heap)
# 从堆中提取结果并按升序排列
result = sorted([num for _, num in min_heap])
# 返回结果字符串
return ",".join(map(str, result))
# 测试
if __name__ == "__main__":
print(solution([1, 1, 1, 2, 2, 3], 2) == "1,2") # 输出: "1,2"
print(solution([1], 1) == "1") # 输出: "1"
print(solution([4, 4, 4, 2, 2, 2, 3, 3, 1], 2) == "2,4") # 输出: "2,4"
代码详解
-
统计频率
使用collections.Counter统计数组中每个元素的出现次数。结果是一个键值对,键是数组元素,值是其出现的次数。freq = Counter(nums) -
最小堆操作
- 遍历
freq.items(),将每个元素及其频率作为元组 ((频率, 元素)) 压入最小堆。 - 如果堆的大小超过 ( k ),则弹出堆顶(即频率最小的元素)。
- 最终堆中保留的就是频率前 ( k ) 高的元素。
min_heap = [] for num, count in freq.items(): heapq.heappush(min_heap, (count, num)) if len(min_heap) > k: heapq.heappop(min_heap) - 遍历
-
排序与格式化
- 从堆中提取出数字,按升序排序。
- 将排序结果转换为字符串并返回。
result = sorted([num for _, num in min_heap]) return ",".join(map(str, result))
时间复杂度分析
-
统计频率:( O(n) )
遍历数组统计频率。 -
堆操作:( O(n \log k) )
遍历字典插入堆,每次操作的复杂度是 ( O(\log k) )。 -
排序结果:( O(k \log k) )
对堆中 ( k ) 个元素进行排序。
总体时间复杂度为: [ O(n) + O(n \log k) + O(k \log k) \approx O(n \log k) ]
测试结果
测试用例 1
nums = [1, 1, 1, 2, 2, 3]
k = 2
- 频率统计:({1: 3, 2: 2, 3: 1})
- 堆中保留:((2, 2), (3, 1))
- 排序后结果:("1,2")
测试用例 2
nums = [1]
k = 1
- 频率统计:({1: 1})
- 堆中保留:((1, 1))
- 排序后结果:("1")
测试用例 3
nums = [4, 4, 4, 2, 2, 2, 3, 3, 1]
k = 2
- 频率统计:({4: 3, 2: 3, 3: 2, 1: 1})
- 堆中保留:((3, 2), (3, 4))
- 排序后结果:("2,4")
总结
以上代码高效地解决了问题,符合 ( O(n \log k) ) 的时间复杂度要求,且测试结果与题目期望一致。如有更大数据的性能测试需求,也可进一步验证堆操作的效率。