题目
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
示例:
示例1:
输入:nums = [1,1,1,2,2,3], k = 2
输出:[1,2]
示例2:
输入:nums = [1], k = 1
输出:[1]
特殊情况
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的
解题思路:
对于找出出现频率前 k 高的元素的问题,可以从几个关键点来理解:
- 统计频率的必要性
问题需求:题目要求找出频率最高的 k 个元素,这就意味着我们必须知道每个元素的出现次数。
数据结构选择:使用哈希表(JavaScript中通常是对象或Map)来存储元素及其频率,是因为哈希表提供了快速的查找和更新操作,平均时间复杂度为O(1)。这对于统计大量数据的频率非常有用。 - 转换为可排序结构
排序需求:为了找出频率最高的元素,我们需要一种方式来比较元素的频率。直接在哈希表中进行排序是困难的,因为哈希表没有定义自然的顺序。
数组转换:将哈希表的键值对转换成数组,使得每个数组元素都是一个 [value, frequency] 的形式,这样就可以使用数组的排序方法。 - 排序的策略
降序排序:为了找出频率最高的元素,我们需要对元素按照频率进行降序排序。这样,数组的前端就会是频率最高的元素。
效率考虑:虽然排序的时间复杂度通常为O(n log n),但它提供了确定性的结果,而且在实践中通常足够快,特别是对于中小规模的数据集。 - 选取前k个元素
切片操作:一旦数组被排序,我们可以使用数组的切片操作来直接获取前 k 个元素。这是非常直接和高效的方法,时间复杂度为O(k)。 - 返回结果
输出格式:题目要求返回元素,而不包括它们的频率,因此在返回结果之前,我们仅提取数组元素的第一个元素(即元素的值),并将其作为一个新的数组返回。
这个问题可以通过几个步骤来解决:
- 统计元素出现的频率:遍历数组
nums
,使用哈希表(JavaScript中的对象或Map
)来统计每个元素出现的次数。 - 将频率信息转换成可排序的结构:将哈希表的键值对转换成一个数组,每个数组元素是一个
[value, frequency]
形式的数组,其中value
是原数组中的元素,frequency
是它的出现次数。 - 根据频率排序:对上述数组按照频率进行排序。由于我们需要频率最高的元素,可以使用降序排序。
- 选取前k个元素:从排序后的数组中取出前
k
个元素。 - 返回结果:返回步骤4中获取的元素列表。
function topKFrequent(nums, k) { // 第一步:统计每个元素的频率。 // 创建一个对象来保存每个数字的出现次数。 const frequencyMap = {}; nums.forEach(num => { // 如果数字已经在频率图中,增加其计数;否则,初始化为1。 frequencyMap[num] = (frequencyMap[num] || 0) + 1; }); // 第二步:将频率图转换为一个包含[element, frequency]对的数组。 // 使用Object.entries将对象转换为键值对数组,然后映射为包含数字和频率的数组。 const freqArray = Object.entries(frequencyMap).map(([num, freq]) => [parseInt(num), freq]); // 第三步:根据频率对数组进行降序排序。 // 使用sort函数,并提供一个比较函数来按频率降序排列。 freqArray.sort((a, b) => b[1] - a[1]); // 第四步:根据频率提取前k个元素。 // 切片数组以获取前k个元素,然后映射回仅包含元素的数组。 const topK = freqArray.slice(0, k).map(item => item[0]); // 第五步:返回结果。 // 最终返回包含前k个最高频率元素的数组。 return topK; } // 测试函数 // const numsExample = [1, 1, 1, 2, 2, 3]; // const kExample = 2; // console.log(topKFrequent(numsExample, kExample)); // 预期输出: [1, 2]
涉及到的小知识点:
1.“||”符号:
- 逻辑或运算符:在C语言等编程语言中,“||”表示逻辑“或”运算符,用于判断多个条件中是否至少有一个成立。当任何一个操作数为真时,结果为真;只有当两个操作数都为假时,结果才为假。具有短路求值特性,即如果第一个操作数为真,则不计算第二个操作数。
2. Object.entries方法:返回对象键值对数组
即:使用Object.entries(frequencyMap)
获取频率图frequencyMap
的所有键值对,每个键值对是一个包含键和值的数组。
3.使用sort
方法排序freqArray:
即:排序依据:数组中元素的第二个值(即频率);排序方式:降序排列
4.slice方法:用于提取原数组的一部分,返回一个新数组,原数组不会被修改。
5.map方法:map方法是JavaScript中的一种数组方法,用于遍历数组并返回一个新数组,该数组的元素为原始数组元素调用函数处理后的值。