[前端]_一起刷leetcode 347. 前 K 个高频元素

258 阅读3分钟

「这是我参与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 <= 105
  • k 的取值范围是 [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%的用户。用计数排序的本质就是时间换空间,只不过提升的快慢需要我们结合业务自己进行衡量。

看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。