[路飞]_leetcode刷题_347. 前 K 个高频元素

162 阅读1分钟

题目

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

示例 1:

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

示例 2:

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

思路1:

哈希表,暴力解法。

申明一个哈希表,用来存储每个值和它出现次数的映射。

遍历数组nums,将每一个值的出现的次数统计出来。

再将这个哈希表转成数组,对数组做排序处理,然后将数组前k项的值return出来即可

代码如下:

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var topKFrequent = function(nums, k) {
    let map = new Map();
    for(const num of nums) {
      map.set(num, (map.get(num) || 0) + 1);
    }

    var arr=Array.from(map);
    arr.sort(function(a,b){return b[1]-a[1]})
    let res = [];
    for(let i=0;i<k;i++){
        res.push(arr[i][0])
    }
    return res
};

复杂度分析

时间复杂度:O(n^2),最坏的情况,每个元素都只出现一次,那么排序这里耗时n^2

空间复杂度:O(n),最坏的情况,每个元素只出现一次,map的size即位数组的长度。

思路2:

小顶堆。

首先,还是要像上面一样,申明一个哈希表,将值与它出现的次数维护在这个哈希表里。

然后我们申明一个小顶堆,for of遍历这个哈希表的entries,此时entry会是一个键值对数组,键为数值,值为数值出现的次数。

  • 将每个键值数组推入小顶堆
  • 入堆后,小顶堆自动会排序,我们让它以出现次数排序,那么出现次数最少的值会浮到最上面
  • 持续推入操作,当小顶堆的节点数大于k时,说明此时堆中刚好有k+1个元素,那么我们将堆顶元素弹出,这时堆内就是刚好出现频率最高的k个元素了。

实现代码如下:

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var topKFrequent = function(nums, k) {
  const map = new Map();
  const res = [];
  for(const num of nums) {
    map.set(num, (map.get(num) || 0) + 1);
  }
  const heap = new MinHeap();
  for (const entry of map.entries()) {
    heap.offer(entry);
    if (heap.size() > k) {
      heap.pop();
    }
  }
  for(let i = heap.size() - 1; i >= 0; i--) {
    res[i] = heap.pop()[0];
  }
  return res;
};


class MinHeap{
    constructor(){
        this.heap = [];
    }
    offer(item){
        this.heap.push(item);
        let index = this.heap.length - 1;
        let parent = Math.floor((index - 1) / 2);
        while(parent >= 0 && this.compare(parent, index) > 0) {
            [this.heap[index], this.heap[parent]] = [this.heap[parent], this.heap[index]];
            index = parent;
            parent = Math.floor((index - 1) / 2);
        }
    }
    pop(){
        const res = this.heap[0];
        this.heap[0] = this.heap.pop();
        let index = 0,left = 1;
        let child = this.compare(left, left + 1) > 0 ? left + 1 : left;
        while(child !== undefined && this.compare(index, child) > 0) {
            [this.heap[index], this.heap[child]] = [this.heap[child], this.heap[index]];
            index = child;
            left = 2 * index + 1;
            child = this.compare(left, left + 1) > 0 ? left + 1 : left;
        }
        return res;
    }
    size(){
        return this.heap.length;
    }
    compare(index1,index2){
        if (this.heap[index1] === undefined) return 1
        if (this.heap[index2] === undefined) return -1
        return this.heap[index1][1]>this.heap[index2][1];
    }
}

复杂度分析:

时间复杂度:O(nlogk),遍历得到哈希表是O(n),随后处理哈希表为O(n),每次遍历,还都会执行小顶堆排序,最后一次为复杂度为O(logk),那么处理哈希表并执行堆排序为O(nlogk),最终取O(nlogk);

空间复杂度:O(n),哈希表空间复杂度为O(n),堆为O(k),取O(n)