力扣[347. 前 K 个高频元素]

44 阅读3分钟

347. 前 K 个高频元素

中等

1.7K

相关企业

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

 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var topKFrequent = function(nums, k) {
    let times = {}
    nums.forEach(num=>{
        if(times[num]!==undefined){
            times[num]++
        }else{
            times[num]=1
        }
    })
    let keys = Object.keys(times)
   keys.sort((a,b)=>{
       return times[b]-times[a]
   })
   return keys.slice(0,k)
};

这里让我们统计出现频次最高的k个高频元素

  • 思路,先循环数组统计每个数字出现的频率,然后根据得到的数字频率进行排序,截取符合条件的k个元素return即可。
  1. 第一步声明一个变量times用于接受统计的各个数字的次数
  2. 循环数字数组统计数字出现的次数,如果该数组在times中出现过,则该数字次数加一,否则在times中新增一个key为当前数字的元素,值为1,这里也可以利用map统计每个元素出现的次数使用map时。注意点:map不能直接排序,可以通过Array.from或展开运算符将Map转化为二维数组进行排序。
  3. 循环完毕后,使用Object.keys()获取times的key,即数字数组中出现的所有的数字,然后对所有出现的数字进行排序keys.sort((a,b)=>{ return times[b]-times[a] })这里使用sort排序,根据之前统计的times,对所有出现的数据进行从大到小的排序。 4.已知题目要求我们返回出现频次最高的k个元素,这里我们直接从0开始截取排序后的keys(出现的所有的数字按照从大到小排序后)k个元素返回即可。

如果是按照从小到大的顺序排的keys,截取keys数组的后k位元素。

进阶算法的话,不会,但是找到个大佬的。

优先队列

思路:循环数组,加入小顶堆,当堆的size超过k时,出堆,循环完成之后,堆中所有的元素就是前k大的数字 复杂度:时间复杂度O(nlogk),循环n次,每次堆的操作是O(logk)。空间复杂度O(k),

作者:晨 链接:leetcode.cn/problems/to… 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    constructor(comparator = (a, b) => a - b, data = []) {
        this.data = data;
        this.comparator = comparator;//比较器
        this.heapify();//堆化
    }

    heapify() {
        if (this.size() < 2) return;
        for (let i = Math.floor(this.size()/2)-1; i >= 0; i--) {
            this.bubbleDown(i);//bubbleDown操作
        }
    }

    peek() {
        if (this.size() === 0) return null;
        return this.data[0];//查看堆顶
    }

    offer(value) {
        this.data.push(value);//加入数组
        this.bubbleUp(this.size() - 1);//调整加入的元素在小顶堆中的位置
    }

    poll() {
        if (this.size() === 0) {
            return null;
        }
        const result = this.data[0];
        const last = this.data.pop();
        if (this.size() !== 0) {
            this.data[0] = last;//交换第一个元素和最后一个元素
            this.bubbleDown(0);//bubbleDown操作
        }
        return result;
    }

    bubbleUp(index) {
        while (index > 0) {
            const parentIndex = (index - 1) >> 1;//父节点的位置
            //如果当前元素比父节点的元素小,就交换当前节点和父节点的位置
            if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {
                this.swap(index, parentIndex);//交换自己和父节点的位置
                index = parentIndex;//不断向上取父节点进行比较
            } else {
                break;//如果当前元素比父节点的元素大,不需要处理
            }
        }
    }

    bubbleDown(index) {
        const lastIndex = this.size() - 1;//最后一个节点的位置
        while (true) {
            const leftIndex = index * 2 + 1;//左节点的位置
            const rightIndex = index * 2 + 2;//右节点的位置
            let findIndex = index;//bubbleDown节点的位置
            //找出左右节点中value小的节点
            if (
                leftIndex <= lastIndex &&
                this.comparator(this.data[leftIndex], this.data[findIndex]) < 0
            ) {
                findIndex = leftIndex;
            }
            if (
                rightIndex <= lastIndex &&
                this.comparator(this.data[rightIndex], this.data[findIndex]) < 0
            ) {
                findIndex = rightIndex;
            }
            if (index !== findIndex) {
                this.swap(index, findIndex);//交换当前元素和左右节点中value小的
                index = findIndex;
            } else {
                break;
            }
        }
    }

    swap(index1, index2) {//交换堆中两个元素的位置
        [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
    }

    size() {
        return this.data.length;
    }
}

var topKFrequent = function (nums, k) {
    const map = new Map();

    for (const num of nums) {//统计频次
        map.set(num, (map.get(num) || 0) + 1);
    }

    //创建小顶堆
    const priorityQueue = new Heap((a, b) => a[1] - b[1]);

    //entry 是一个长度为2的数组,0位置存储key,1位置存储value
    for (const entry of map.entries()) {
        priorityQueue.offer(entry);//加入堆
        if (priorityQueue.size() > k) {//堆的size超过k时,出堆
            priorityQueue.poll();
        }
    }

    const ret = [];

    for (let i = priorityQueue.size() - 1; i >= 0; i--) {//取出前k大的数
        ret[i] = priorityQueue.poll()[0];
    }

    return ret;
};



作者:晨
链接:https://leetcode.cn/problems/top-k-frequent-elements/solutions/1119211/347-qian-k-ge-gao-pin-yuan-su-by-chen-we-173k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。