一起刷LeetCode——前k个高频元素(小顶堆)

105 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

前k个高频元素

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

来源:力扣(LeetCode) 链接:leetcode.cn/problems/to…

分析

Hash Map

  • 根据题意,比较容易想到对数组进行遍历,生成一个元素出现次数数组,找出前k个高频的元素也就是数组按照出现次数从大到小取前k个即可得到答案
代码
var topKFrequent = function(nums, k){
    let map = new Map()
    for(let i=0;i<nums.length;i++){
        if(map.has(nums[i]){
            map.set(nums[i],map.get(nums[i])+1)
        } else {
            map.set(nums[i],1)
        }
    }
    let values = map.values()
    let keys = map.keys()
    let res = []
    for(let j=0;j<k;j++){
        let index = values.indexOf(Math.max(...values))
        value[index] = null
        res.push(keys[index])
    }
    return res
}

数组自动排序

  • 在遍历数组的时候,自动对数组进行排序,直接去排好序数组的最大k个
代码
var topKFrequent = function(nums, k){
    let arr = []
    let sort = []
    let res = []
    for(let i=0;i<nums.length;i++){
        arr[nums[i]] = (arr[nums[i]] || 0) + 1
    }
    for(let item in arr){
        sort[arr[item]] ? sort[arr[item]].push(item) : sort[arr[item]] = [item]
    }
    for(let i = sort.length -1 ;i>=0;i--){
        if(sort[i]){
            res.push(...sort[i])
        }
    }
    return res
}

小顶堆

  • 使用堆来遍历出现次数数组,当堆内元素小于k,直接插入堆中,如果堆内个数等于k,检查堆顶与当前遍历值出现的次数谁大,如果堆顶大,说明有至少k个数字的出现次数比当前遍历到的次数大,舍弃当前的值,反之就插入堆中,遍历后,堆中的元素就是前k个高频元素
代码
var topKFrequent = function(nums, k) {
    let freq = {}, heap = [];
    let left = i => i * 2 + 1 < k ? i * 2 + 1 : i;
    let right = i => i * 2 + 2 < k ? i * 2 + 2 : i;
    let swap = (i,j) => { [heap[i], heap[j]] = [heap[j], heap[i]]; return j}
    let heapify = () => {
        const max_lvl = Math.floor(Math.log2(k));
        
        for (let lvl = max_lvl - 1; lvl >= 0; lvl--) {
            for (let i = Math.pow(2, lvl) - 1; i < Math.pow(2, lvl+1) - 1; i++) {
                let cur = i;
                let l = left(cur);
                let r = right(cur);
                // satisfy Min Heap rule
                while (heap[cur][1] > heap[l][1] || 
                       heap[cur][1] > heap[r][1]
                ) {
                    if (heap[l][1] < heap[r][1]
                    ) {
                        cur = swap(cur, l);                        
                    } else {
                        cur = swap(cur, r);                        
                    }
                    l = left(cur);
                    r = right(cur);
                }
            }
        }
    }
    
    for (let i = 0; i < nums.length; i++) {
        freq[nums[i]] = (freq[nums[i]] || 0) + 1;
    }
    
    for (let val in freq) {        
        if (heap.length < k) {
            heap.push([val, freq[val]]);    
            continue;
        }        
        heapify();    
        
        if (freq[val] > heap[0][1]) {
            heap[0] = [val, freq[val]];
        }
    }
    heapify();    
    
    for (let i = 0; i < heap.length; i++) {
        heap[i] = heap[i][0];
    }
    
    return heap;    
}

总结

  • 我们常说堆栈,其实解题的时候更多的是有意识的用栈、队列,比较少用堆
  • JavaScript在构建一个堆的时候,确实不是很便利,但是我们仍需要有堆的思想
  • 今天也是有收获的一天