McElfresh Blog

Go, PostgreSQL, MySQL, Ruby, Rails, Sinatra, etc.

Go Heapsort

Posted at — Apr 28, 2026

Go provides heap.Interface as a means to create a heap backed by a slice.

Run it on Go Playground

Here we make a heap from an []int:

  1package main
  2
  3import (
  4  "container/heap"
  5  "fmt"
  6)
  7
  8// create type myHeap, based on []int
  9type myHeap []int
 10
 11// heap.Interface requires that myHeap
 12// implement the three methods from
 13// sort.Interface, plus Push and Pop
 14// (below). Here we are creating a max
 15// heap, so we do m[i] > m[j]. We'd do
 16// m[i] < m[j] if we were creating a min
 17// heap. As you'll see in the heapsort
 18// method, a max heap sorts low -> high
 19// values.
 20func (m myHeap) Less(i, j int) bool {
 21  return m[i] > m[j]
 22}
 23
 24func (m myHeap) Swap(i, j int) {
 25  m[i], m[j] = m[j], m[i]
 26}
 27
 28func (m myHeap) Len() int {
 29  return len(m)
 30}
 31
 32// Push requires a pointer receiver because
 33// append may allocate a new backing array,
 34// changing the slice header. A value receiver
 35// would leave the caller's header unchanged.
 36func (m *myHeap) Push(x interface{}) {
 37  *m = append(*m, x.(int))
 38}
 39
 40// Pop requires a pointer receiver because we
 41// shorten the slice (reduce len by 1). A
 42// value receiver would only shorten the
 43// local copy — the caller's slice header
 44// would be unchanged.
 45func (m *myHeap) Pop() interface{} {
 46  old := *m
 47  n := len(old)
 48  x := old[n-1]
 49  *m = old[:n-1]
 50  return x
 51}
 52
 53// We mutate the array the slice points to in-place.
 54// The slice header (pointer/len/cap) is copied when
 55// assigned, but the backing array is shared.
 56func heapsort(sl []int) {
 57  // copies the slice header; h and sl
 58  // share the same backing array
 59  h := myHeap(sl)
 60
 61  // heap.Init operates in-place on the slice
 62  // (no array copy) to build a heap.
 63  // note we pass &h not &sl
 64  // because h is the myHeap type that implements heap.Interface
 65  // sl is []int which does not implement heap.Interface
 66  heap.Init(&h)
 67  // Now h and sl are distinct slice headers that
 68  // refer to the same array.
 69  for i := len(sl) - 1; i >= 0; i-- {
 70    // heap.Pop swaps the root with the last element,
 71    // shortens the active heap, and returns the removed
 72    // root (the current max). That max already sits
 73    // at the rightmost position, so assigning
 74    // sl[i] = heap.Pop(&h).(int)
 75    // writes into a slot no longer needed by the active heap.
 76    sl[i] = heap.Pop(&h).(int)
 77  }
 78}
 79
 80func main() {
 81  input := []int{5, 1, 2, 7}
 82  h := myHeap{}
 83  heap.Init(&h)
 84  for _, n := range input {
 85    heap.Push(&h, n)
 86  }
 87  fmt.Println(h)
 88  // [7 5 2 1]
 89  heap.Push(&h, 3)
 90  el := heap.Pop(&h)
 91  fmt.Println(el)
 92  // 7
 93  heap.Push(&h, 10)
 94  el = heap.Pop(&h)
 95  fmt.Println(el)
 96  // 10
 97  heapsort(h)
 98  fmt.Println(h)
 99  // [1 2 3 5]
100}