【算法】前 K 个高频元素

155 阅读1分钟

难度:中等

题目:

给你一个整数数组 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是数组大小。

解题思路:

对于找出出现频率前 k 高的元素的问题,可以从几个关键点来理解:

  1. 统计频率的必要性
    问题需求:题目要求找出频率最高的 k 个元素,这就意味着我们必须知道每个元素的出现次数。
    数据结构选择:使用哈希表(JavaScript中通常是对象或Map)来存储元素及其频率,是因为哈希表提供了快速的查找和更新操作,平均时间复杂度为O(1)。这对于统计大量数据的频率非常有用。
  2. 转换为可排序结构
    排序需求:为了找出频率最高的元素,我们需要一种方式来比较元素的频率。直接在哈希表中进行排序是困难的,因为哈希表没有定义自然的顺序。
    数组转换:将哈希表的键值对转换成数组,使得每个数组元素都是一个 [value, frequency] 的形式,这样就可以使用数组的排序方法。
  3. 排序的策略
    降序排序:为了找出频率最高的元素,我们需要对元素按照频率进行降序排序。这样,数组的前端就会是频率最高的元素。
    效率考虑:虽然排序的时间复杂度通常为O(n log n),但它提供了确定性的结果,而且在实践中通常足够快,特别是对于中小规模的数据集。
  4. 选取前k个元素
    切片操作:一旦数组被排序,我们可以使用数组的切片操作来直接获取前 k 个元素。这是非常直接和高效的方法,时间复杂度为O(k)。
  5. 返回结果
    输出格式:题目要求返回元素,而不包括它们的频率,因此在返回结果之前,我们仅提取数组元素的第一个元素(即元素的值),并将其作为一个新的数组返回。

JavaScript实现:

/** * @param {number[]} nums * @param {number} k * @return {number[]} */var topKFrequent = function(nums, k) {    // 1、先声明一个map,用来存储相同的整数个数    // 2、遍历nums,然后比较相同的元素,然后添加到对应的数组中    // 3、然后再把map转为数组,再进行排序,再进行截取,再进行抽取数组中的第一项组成新的数组    let [map, len] = [new Map(), nums.length]    for (let i = 0; i < len; i++) {        let num = nums[i]        if (map.has(num)) {            map.set(num, map.get(num) + 1)        } else {            map.set(num, 1)        }    }    // 这里一定要进行降序排列,所以是sort((a, b) => b[1] - a[1])    let arr = [...map.entries()].sort((a, b) => b[1] - a[1]).slice(0, k).map(item => item[0])    return arr;};