题目

题目来源:力扣(LeetCode)

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

方法一:快速排序

我们可以使用基于快速排序的方法,求出「出现次数数组」的前 k 大的值。

  1. 首先用哈希表记录每个数字出现的次数
  2. 对每个数及对应次数通过快排进行排序,返回前k个
  3. 在对数组 arr[l…r] 做快速排序的过程中,我们首先将数组划分为两个部分 arr[i…q−1] 与 [q+1…j],并使得 arr[i…q−1] 中的每一个值都不超过 arr[q],且 arr[q+1…j] 中的每一个值都大于arr[q]。

于是,我们根据 k 与左侧子数组 arr[i…q−1] 的长度(为q−i)的大小关系:

  • 如果 k≤q−i,则数组 arr[l…r] 前 k 大的值,就等于子数组 arr[i…q−1] 前 k 大的值。
  • 否则,数组arr[l…r] 前 k 大的值,就等于左侧子数组全部元素,加上右侧子数组 arr[q+1…j] 中前 k−(q−i) 大的值。
  1. /**
  2. * @param {number[]} nums
  3. * @param {number} k
  4. * @return {number[]}
  5. */
  6. var topKFrequent = function (nums, k) {
  7. // 统计出现次数
  8. const map = {};
  9. for (const n of nums) {
  10. n in map || (map[n] = 0);
  11. map[n]++;
  12. }
  13. const list = Object.entries(map);
  14. quickSelect(list, k, 0, list.length - 1, function (item, pivot) {
  15. return item[1] >= pivot[1];
  16. });
  17. return list.slice(0, k).map(el => el[0]);
  18. };
  19. /**
  20. * 把 arr[r] 当成是 pivot
  21. * 把大于等于 pivot 的数字放到左边
  22. * 把小于 pivot 的数字放到右边
  23. * @param {number[]} arr
  24. * @param {number} l
  25. * @param {number} r
  26. */
  27. function partition(arr, l, r, comparator) {
  28. if (typeof comparator != 'function') {
  29. comparator = function (num, pivot) {
  30. return num >= pivot;
  31. };
  32. }
  33. let i = l;
  34. for (let j = l; j < r; j++) {
  35. if (comparator(arr[j], arr[r])) {
  36. [arr[i], arr[j]] = [arr[j], arr[i]];
  37. i++;
  38. }
  39. }
  40. // 将 pivot 换到分界点
  41. [arr[i], arr[r]] = [arr[r], arr[i]];
  42. // 返回 pivot 的下标
  43. return i;
  44. }
  45. /**
  46. * 寻找第 k 大元素
  47. * 如果 pivot 的下标刚好是 k - 1,那我们就找到了
  48. * 如果下标大于 k - 1,那就在 [left, pivotIndex - 1] 这段找第 k 大元素
  49. * 如果下标小于 k - 1,那就对 [pivotIndex + 1, right] 这段找第 k - pivotIndex + left
  50. - 1 大元素
  51. * @param {number[]} list
  52. * @param {number} left
  53. * @param {number} right
  54. * @param {number} k
  55. * @param {function} comparator
  56. */
  57. function quickSelect(list, k, left = 0, right = list.length - 1, comparator) {
  58. if (left >= right) return list[left];
  59. const pivotIndex = partition(list, left, right, comparator);
  60. if (pivotIndex - left === k - 1) return list[pivotIndex];
  61. else if (pivotIndex - left > k - 1)
  62. return quickSelect(list, k, left, pivotIndex - 1, comparator);
  63. else {
  64. return quickSelect(
  65. list,
  66. k - pivotIndex + left - 1,
  67. pivotIndex + 1,
  68. right,
  69. comparator,
  70. );
  71. }
  72. }