Go标准库container/heap不提供现成堆类型,而是通过实现heap.Interface接口(含Len、Less、Swap、Push、Pop五方法)配合heap.Init/Push/Pop函数使用;需传指针、必调Init、Pop非取顶、无并发安全。

Go 语言标准库 container/heap 并不直接提供一个“堆类型”,而是定义了一套接口和通用操作函数,要求你自行实现 heap.Interface(本质是 sort.Interface 的扩展),再配合 heap.Init、heap.Push、heap.Pop 等函数使用。核心在于:**你提供底层切片 + 满足堆序的比较逻辑,heap 包负责维护堆结构。**
定义可堆化的数据类型(实现 heap.Interface)
要让自定义类型支持堆操作,必须实现以下五个方法:
- Len():返回元素个数
-
Less(i, j int):决定堆序(如最小堆则
a[i] 时返回 true) - Swap(i, j int):交换索引 i 和 j 处元素
- Push(x interface{}):将 x 追加到切片末尾(注意:x 是 interface{},需类型断言)
- Pop() interface{}:移除并返回最后一个元素(heap.Pop 内部会调用它来获取待交换/返回的值)
常见错误:忘记在 Push 中用 *h = append(*h, x.(YourType)),或在 Pop 中写成 res := (*h)[len(*h)-1]; *h = (*h)[:len(*h)-1]; return res —— 顺序不能反,否则切片截取会出错。
实现最小堆优先队列(按数值升序)
以整数最小堆为例,支持入队、出队、查看队首:
立即学习“go语言免费学习笔记(深入)”;
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } // 小值优先 → 最小堆
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
item := old[n-1]
*h = old[0 : n-1]
return item
}
// 使用示例
func main() {
h := &IntHeap{}
heap.Init(h)
heap.Push(h, 3)
heap.Push(h, 1)
heap.Push(h, 4)
fmt.Println(heap.Pop(h)) // 1
fmt.Println(heap.Pop(h)) // 3
fmt.Println(heap.Pop(h)) // 4
}
实现最大堆或按字段排序的结构体堆
只需修改 Less 方法即可切换行为:
- 最大堆:把
Less(i,j)改为return h[i] > h[j] - 结构体按字段排序(如任务按优先级):
type Task struct { Name string Priority int } type TaskHeap []Task func (h TaskHeap) Less(i, j int) bool { return h[i].Priority < h[j].Priority } // 优先级小的先执行
关键注意事项与技巧
切片必须是指针传入:所有 heap.Xxx 函数都接收 interface{},实际期望的是指向实现了 heap.Interface 的变量的指针(如 &IntHeap{}),否则修改不会反映到原切片。
初始化不可省略:即使切片已有序或为空,首次使用前也必须调用 heap.Init(h),它会按堆序重排底层切片。
Pop 不等于取顶:heap.Pop 是删除并返回堆顶,若只想看顶元素,用 h[0](前提是 h.Len() > 0)。
线程不安全:标准 heap 包无并发保护,多 goroutine 访问需自行加锁。










