1、问题
1.1 问题描述
给你一个整数数组 nums 和一个整数 k,请你用一个字符串返回其中出现频率前 k 高的元素。请按升序排列。
你所设计算法的时间复杂度必须优于 O(n log n),其中 n 是数组大小。
1.2 输入
- nums: 一个正整数数组
- k: 一个整数
1.3 返回
返回一个包含 k 个元素的字符串,数字元素之间用逗号分隔。数字元素按升序排列,表示出现频率最高的 k 个元素。
1.4 参数限制
- 1 <= nums[i] <= 10^4
- 1 <= nums.length <= 10^5
- k 的取值范围是 [1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
1.5 测试样例
样例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"
2、思路
为了满足时间复杂度的要求,我们的大体思路可以分为以下几个步骤:
- 统计频率:遍历数组
nums,统计每个数字的出现频率。 - 排序频率:将统计到的频率按降序排序。
- 选择前 k 个元素:从排序后的频率中选择前
k个元素。 - 升序排序:对这
k个元素按升序排序。 - 转换为字符串:将结果转换为字符串格式,并用逗号分隔。
明确了整体思路之后,就开始对每个步骤进行实现,具体如下:
2.1 统计频率
我们使用一个字典 freq_dict 来存储每个数字的频率。遍历数组 nums,对于每个数字,如果它不在字典中,则将其添加到字典中并设置频率为 1;如果已经在字典中,则将其频率加 1。
# 用于存储每个数字的频率
freq_dict = {}
# 统计每个数字的频率
for num in nums:
if num not in freq_dict:
freq_dict[num] = 1
else:
freq_dict[num] += 1
2.2 排序频率
将字典 freq_dict 按频率降序排序。这里使用 sorted 函数,并指定频率值,reverse=True 表示降序排序。
# 按频率降序排序字典项
freq_dict = sorted(freq_dict.items(), key=lambda d: d[1], reverse=True)
2.3 选择前 k 个元素
从排序后的列表中选择前 k 个元素。这里我们只关心元素的值,因此使用列表推导式提取前 k 个元素的值。
# 取前 k 个频率最高的元素
result = [item[0] for item in freq_dict[:k]]
2.4 升序排序
对这 k 个元素按升序排序。这里再次使用 sorted 函数,并指定 reverse=False 表示升序排序。
# 对结果按升序排序
result = sorted(result, reverse=False)
2.5 转换为字符串
将结果列表中的每个元素转换为字符串,并用逗号连接成一个字符串。
# 将结果转换为字符串
result = [str(item) for item in result]
return ','.join(result)
3、代码
整体代码如下:
def solution(nums, k):
result = []
freq_dict = {} # 用于存储每个数字的频率
# 统计每个数字的频率
for num in nums:
if num not in freq_dict:
freq_dict[num] = 1
else:
freq_dict[num] += 1
# 按频率降序排序字典项
freq_dict = sorted(freq_dict.items(), key=lambda d: d[1], reverse=True)
# 取前 k 个频率最高的元素
result = [item[0] for item in freq_dict[:k]]
# 对结果按升序排序
result = sorted(result, reverse=False)
# 将结果转换为字符串
result = [str(item) for item in result]
return ','.join(result)
4、运行结果
可以看到提交通过,说明我们的方法和代码都没有问题。
5、总结分析
这5个步骤的时间复杂度分析如下:
- 统计频率:遍历数组
nums,时间复杂度为O(n)。 - 排序频率:排序字典项的时间复杂度为
O(n log n)。 - 选择前 k 个元素:时间复杂度为
O(k)。 - 升序排序:对
k个元素排序的时间复杂度为O(k log k)。 - 转换为字符串:时间复杂度为
O(k)。
总体时间复杂度为 O(n + n log n + k log k),由于 k 通常远小于 n,因此可以近似为 O(n log n)。虽然这个时间复杂度不严格优于 O(n log n),但在实际应用中,k 通常较小,因此这个算法在大多数情况下还是比较高效的。
最后,也是希望同学们可以多多刷题,提升自己的思维能力和编码水平。