前 K 个高频元素
题目
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2] 示例 2:
输入: nums = [1], k = 1 输出: [1]
提示:
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。你可以按任意顺序返回答案。
解题思路
哈希表-优先队列
这道常考题的经典做法,必须掌握!
- 先用 哈希表 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率
- 维护一个元素数目为
K
的优先队列
这里要前K个高频元素,也就是按从大到小排序,所以使用最小堆。(同理,若要从小到大排序,则使用最大堆)
- 每次都将新的元素与堆顶元素(堆中频率最小的元素)进行比较;
- 如果新的元素的频率比堆顶端的元素大,则弹出堆顶端的元素,将新的元素添加进堆中;
- 所以最终,堆中的 k 个元素即为前 k 个高频元素。
代码
方法1:
//go // go中没有自带的优先队列,需要自己实现小根堆 func topKFrequent(nums []int, k int) []int { if k == 0 || len(nums) == 0 { return make([]int, 0) } // 1. 初始化map m := make(map[int]int) for _, v := range nums { m[v] = m[v] + 1 } // 2. 放到小根堆里面 h := &NodeHeap{} topK := min(k, len(m)) size := 0 for k, v := range m { if size < topK { heap.Push(h, &Node{ val: k, times: v, }) size++ } else { if v > (*h)[0].times { heap.Pop(h) heap.Push(h, &Node{ val: k, times: v, }) } } } // 3.收集答案 res := make([]int, 0, topK) for i := 0; i < topK; i++ { res = append(res, heap.Pop(h).(*Node).val) } return res } type Node struct { val int times int } type NodeHeap []*Node func (h NodeHeap) Len() int { return len(h) } // 小根堆 func (h NodeHeap) Less(i, j int) bool { return h[i].times < h[j].times } func (h NodeHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *NodeHeap) Push(x interface{}) { *h = append(*h, x.(*Node)) } func (h *NodeHeap) Pop() interface{} { old := *h n := len(old) x := old[n-1] *h = old[0 : n-1] return x } func min(a, b int) int { if a < b { return a } return b }
方法二:
func topKFrequency(nums []int, k int) []int{ if k <= 0 || len(nums) < k { return make([]int, 0) } mapCount := make(map[int]int) var sK []int for _, v := range nums { if _, ok := mapCount[v]; ok { mapCount[v] += 1 } else { mapCount[v] = 1 sK = append(sK, v) } } sort.SliceStable(sK, func(i, j int) bool { return mapCount[sK[i]] > mapCount[sK[j]] }) return sK[:k] }
地址:https://mp.weixin.qq.com/s/OuvPhV-UOWYvrtX-O2Xiig
small_lei_it 技术无止境,追求更高。