前 K 个高频元素-学习使用小顶堆

125 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述

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

输入:

nums = [1,1,1,2,2,3], k = 2

输出:

[1,2]

提示:

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

二、思路分析

思路一、可以对 nums 数组中的元素进行计数,并将计数结果存入数组,之后对计数数组进行由大到小排序,并找出前 k 个计数结果所对应的元素即可。此思路比较容易实现,但由于可能有O(n)个不同的出现次数,所以复杂度会达到O(nlogn)。

思路二、可以利用小顶堆来进行求解,当得到计数数组后,维护一个小顶堆,遍历计数数组。若堆中元素小于 k 个则直接插入,否则弹出堆顶元素,保证堆中元素不超过 k 个。

本题中使用小顶堆我们需要重写接口

type Interface interface{
	sort.Interface
        
        //向堆中添加元素
	Push(x interface{})
	
        //弹出堆中元素
        Pop() interface{}
}

其中 sort.Interface 包括三个方法

type Interface interface{
	//获取堆长度
        Len() int
	
        //根据需要对堆进行排序
        Less(i, j int) bool
	
        //交换堆中索引为i和j的元素
        Swap(i, j int)
}

三、代码

func topKFrequent(nums []int, k int) []int {
    temp := make(map[int]int)
    for i:=0; i<len(nums); i++{
        temp[nums[i]] ++
    }
    h := &Heap{}
    heap.Init(h)
    for key, val := range temp{
        heap.Push(h, [2]int{key,val})
        if h.Len() > k{
            heap.Pop(h)
        }
    }
    res :=make([]int,k)
    for i:=0; i < k; i++{
        res[k-i-1] = heap.Pop(h).([2]int)[0]
    }
    return res
}

type Heap [][2]int

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

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

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

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

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

四、总结

通过本题学习了如何使用go语言实现小顶堆,并对堆中元素进行操作。并通过小顶堆将算法的时间复杂度降低为 O(logk),算法的空间复杂度降低为 O(n)。