刷题使我快乐,满脸开心.jpg

  • 来源:力扣(LeetCode)
  • 链接:https://leetcode.cn/problems/top-k-frequent-elements/
  • 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 10^5
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

思路

经典 TOPK,应该会很容易往堆上联想,我开始也是直接写的堆排,过了,但是时间复杂度排名比较低,所以感觉应该还有更快方法。

继续思考别的思路,在统计了数字频次的基础上,突然发现 桶排序 的思路似乎很适合:

  • 这里我们可以根据频次分桶,从频次高到低,自然有了一个排序
  • 但是桶内元素我们并不需要再排序了,直接照单全收即可
  • 题目保证了结果集唯一,所以不用考虑桶内元素全收之后,结果集个数多余 k 的情况

这样一来,复杂度降低到了 O(n),当然,空间复杂度肯定会高一些了

大体思路就是这样了,上代码~

代码

桶排序

func topKFrequent(nums []int, k int) []int {

	// 统计 num => count
	countMap := make(map[int]int, len(nums))
	for _, num := range nums {
		countMap[num]++
	}

	// 桶排序 count => []{num1, num2, ...}
    // 注意初始化,会产生很多空桶
	bucket := make([][]int, len(nums)+1)
	for num, count := range countMap {
		if bucket[count] == nil {
			bucket[count] = make([]int, 0)
		}
		bucket[count] = append(bucket[count], num)
	}

	// 从大到小看桶里是否非空,非空则加入结果集,够数了就退出
	res := make([]int, 0, k)
	for i := len(bucket) - 1; i >= 0; i-- {
		// 非空桶跳过
		if len(bucket[i]) > 0 {
			res = append(res, bucket[i]...)
            // 够数了就撤
			if len(res) >= k {
				break
			}
		}
	}
	return res
}

堆排

type minheap [][]int

func (h minheap) Less(i, j int) bool {
	return h[i][1] < h[j][1]
}

func (h minheap) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
}

func (h minheap) Len() int {
	return len(h)
}

func (h *minheap) Push(x interface{}) {
	*h = append(*h, x.([]int))
}

func (h *minheap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

func topKFrequent(nums []int, k int) []int {

	kvMap := make(map[int]int, len(nums))
	for _, v := range nums {
		kvMap[v]++
	}

	h := &minheap{}
	for kNum, v := range kvMap {
		heap.Push(h, []int{kNum, v})
		if h.Len() > k {
			heap.Pop(h)
		}
	}

	res := make([]int, 0, k)
	for i := 0; i < k; i++ {
		res = append(res, (*h)[i][0])
	}
	return res
}