「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
题目:
347. 前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 105k的取值范围是[1, 数组中不相同的元素的个数]- 题目数据保证答案唯一,换句话说,数组中前
k个高频元素的集合是唯一的
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n **是数组大小。
这道题目根我之前写过的计数排序的文章非常相似,不过它在原本的计数排序基础上多加了一轮排序,不了解计数排序的小伙伴可以看看我之前的这篇文章 三分钟了解js实现计数排序juejin.cn/post/702670…
思路:
- 先计算出当前数组的最大值和最小值,根据最大值-最小值来创建我们的计数数据,这样子可以达到节省空间的效果。当然如果一开始确定数值的取值范围的话可以忽略这一步;
- 遍历一轮数组,把每个数字出现的次数统计一遍;
- 对统计好的数组做个排序,出现次数高的排前面。当然对于这道题目来说我们有更好的做法,直接写个方法取出最大元素,调用K次即可
实现:
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var topKFrequent = function(nums, k) {
const n = nums.length;
// 如果想空间换时间可以忽略这一步, 直接创建一个长度为n的数组
let maxValue = minValue = nums[0];
for (let i = 1; i < n; i++) {
maxValue = Math.max(maxValue, nums[i]);
minValue = Math.min(minValue, nums[i]);
}
let total = maxValue - minValue + 1;
let arr = new Array(total).fill(0);
// 记录每个元素出现的次数
for (let i = 0; i < n; i++) {
arr[nums[i] - minValue] += 1;
}
let result = [];
// 执行k轮,这一步比传统排序省掉了n-k的时间
while (k > 0) {
let cur = findMaxValue(arr);
result.push(cur + minValue);
k--;
}
return result;
};
// 取出数组中的最大值
var findMaxValue = function(nums) {
const n = nums.length;
let maxValue = nums[0], maxIndex = 0;
for (let i = 1; i < n; i++) {
if (nums[i] > maxValue) {
maxValue = nums[i];
maxIndex = i;
}
}
// 取出了当前节点的最大值后不要直接删除节点,不然会影响下一轮
nums[maxIndex] = 0;
return maxIndex;
};
总结:
这道题目如果想求快,把第一轮求最大值和最小值的方法省略掉,直接创建一个长度为N的数组,这样子做可以用空间换时间,达到时间击败90%+的用户, 用现在的解法可以击败70%+的用户以及空间击败100%的用户。用计数排序的本质就是时间换空间,只不过提升的快慢需要我们结合业务自己进行衡量。
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。