0.前言
堆是一种树形数据结构(完全二叉树),分为大顶堆和小顶堆,顾名思义,大顶堆就是堆顶(第一个元素)始终存放的是这组元素中的最大元素,小顶堆就是堆顶元素是最小元素。如果需要从一组对象中查找最大值或最小值,且元素会动态增加,使用堆能够高效率的完成需求。GO中标准库提供了heap的功能,一起看看如何使用吧。
1.GO语言中的Heap包使用方法
使用Heap,主要分为三步:
- 定义堆结构体
- 实现heap中Interface接口
- 初始化堆并使用堆
直接上代码:
package main
import (
"container/heap"
"fmt"
)
//定义堆结构体
type HeapInts []int
// 实现Interface接口
// 其中包含 sort中的Interface接口 和push、pop接口
// 注意 push和pop接口的接收者必须是指针
func (h HeapInts) Len() int {
return len(h)
}
//此处是大顶堆,如果是 < 即变成小顶堆
func (h HeapInts) Less(i, j int) bool {
return h[i] > h[j]
}
func (h HeapInts) Swap(i, j int) {
h[i],h[j]=h[j],h[i]
}
func (h *HeapInts) Push(x interface{}) {
*h =append(*h,x.(int))
}
func (h *HeapInts) Pop() interface{} {
temp:=(*h)[len(*h)-1]
*h=(*h)[:len(*h)-1]
return temp
}
func main() {
var h HeapInts
h=append(h,2,1,3)
fmt.Println(h)
//初始化堆
heap.Init(&h)
fmt.Println(h)
//向堆中插入元素
heap.Push(&h,9)
heap.Push(&h,5)
fmt.Println(h)
//弹出堆顶元素
x:=heap.Pop(&h)
fmt.Println(x)
fmt.Println(h)
}
2.一些使用堆解决的算法题
leetcode上有很多可以用堆解决的题目,这里放出两题:
2.1 前K个高频元素
在本题中,堆中每个节点存储一个数的值和数的出现次数,出现次数用于排序,值用于得到答案。
首先使用map记录所有元素的出现次数,然后根据上述的构建堆的步骤构建堆,用于存放map中的数据,此处构建的是大顶堆,最后Pop出K个元素,即是答案,代码如下:
type mm struct {
k int
v int
}
type ms []mm
func (m ms) Len() int {
return len(m)
}
//根据v值进行排序
func (m ms) Less(i, j int) bool {
return m[i].v > m[j].v
}
func (m ms) Swap(i, j int) {
m[i],m[j]=m[j],m[i]
}
func (m *ms) Push(x interface{}) {
*m=append(*m,x.(mm))
}
func (m *ms) Pop() interface{} {
temp:= (*m)[len(*m)-1]
*m=(*m)[:len(*m)-1]
return temp
}
func topKFrequent(nums []int, k int) []int {
var result []int
m:=make(map[int]int)
for _,v:=range nums{
m[v]++
}
var h ms
for k,v:=range m{
h=append(h,mm{k: k,v: v})
}
//初始化堆
heap.Init(&h)
//得到答案
for i:=0;i<k;i++{
result=append(result,heap.Pop(&h).(mm).k)
}
return result
}
2.2 滑动窗口的最大值
在本题中,堆的数据结构中,每个节点存储一个数的下标和数的值。值用于排序,下标用于判断是否在滑动窗口之中。具体流程如下:
- 首先使用第一个滑动窗口的值来构建堆。
- 往右移动一格窗口,将新加入的节点入堆。然后对堆顶元素进行检测(使用k),如果此元素处于滑动窗口中,保存到结果,继续移动窗口;如果不处于窗口中,弹出元素,检测下一个堆顶元素,直到找到一个堆中元素。
- 重复第二步直到遍历完整个数组,返回结果
代码如下:
type mm struct {
k int
v int
}
type ms []mm
func (m ms) Len() int {
return len(m)
}
func (m ms) Less(i, j int) bool {
return m[i].v > m[j].v
}
func (m ms) Swap(i, j int) {
m[i],m[j]=m[j],m[i]
}
func (m *ms) Push(x interface{}) {
*m=append(*m,x.(mm))
}
func (m *ms) Pop() interface{} {
temp:= (*m)[len(*m)-1]
*m=(*m)[:len(*m)-1]
return temp
}
func maxSlidingWindow(nums []int, k int) []int {
var h ms
var result []int
for i := 0; i < k; i++ {
h=append(h,mm{k: i,v: nums[i]})
}
heap.Init(&h)
result=append(result,h[0].v)
for i := k; i < len(nums); i++ {
heap.Push(&h, mm{k: i, v: nums[i]})
for {
if h[0].k >= i - k + 1 {
result = append(result, h[0].v)
break
}else{
heap.Pop(&h)
}
}
}
return result
}