The container/heap package in Go provides a convenient means to implement a priority queue, which is a data structure where each element has a priority. Elements are dequeued in order of their priority, rather than in simple insertion order. In Go, you can create a priority queue by defining a heap interface and using the functions provided in this package to maintain the heap properties.
Table of Contents
Understanding the Heap Interface
To start with, you need to define a type that implements the heap.Interface, which is comprised of three functions: Len(), Less(i, j int) bool, Swap(i, j int), in addition to the methods Push(x interface{}) and Pop() interface{}.
package main
import (
"container/heap"
"fmt"
)
// An Item is something we manage in a priority queue.
type Item struct {
value string // The value of the item; arbitrary.
priority int // The priority of the item in the queue.
index int // The index of the item in the heap.
}
// A PriorityQueue implements heap.Interface and holds Items.
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
return pq[i].priority > pq[j].priority
}
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*Item)
item.index = n
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
old[n-1] = nil // avoid memory leak
item.index = -1 // for safety
*pq = old[0 : n-1]
return item
}
Using the Priority Queue
Once you've implemented the interface, you can start using your priority queue. You'll want to use the heap functions heap.Init, heap.Push, and heap.Pop to manipulate your priority queue.
func main() {
// Some items and their priorities.
items := map[string]int{
"banana": 3, "apple": 4, "pear": 2,
}
// Create a priority queue, put the items in it, and
// establish the priority queue (heap) invariants.
pq := make(PriorityQueue, len(items))
i := 0
for value, priority := range items {
pq[i] = &Item{
value: value,
priority: priority,
index: i,
}
i++
}
heap.Init(&pq)
// Insert a new item and then modify its priority.
item := &Item{
value: "orange",
priority: 1,
}
heap.Push(&pq, item)
pq.update(item, item.value, 5)
// Take the items out; they arrive in decreasing priority order.
for pq.Len() > 0 {
item := heap.Pop(&pq).(*Item)
fmt.Printf("%s with priority %d\n", item.value, item.priority)
}
}
// update modifies the priority and value of an Item in the queue.
func (pq *PriorityQueue) update(item *Item, value string, priority int) {
item.value = value
item.priority = priority
heap.Fix(pq, item.index)
}
Conclusion
Using the container/heap package, you can efficiently maintain a priority queue tailored to your application's needs. This package significantly simplifies the creation and management of heaps in Go, offering a robust foundation for priority queue operations.