heap

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 15, 2023 License: MIT Imports: 5 Imported by: 0

README

GoDoc License

Heap

A generic implementation of min and max binary heaps in Go with an interface suitable for use as a priority queue.

  • Use with types that satisfy constraints.Comparable, or define a Cmp method for your type.
  • Choose min or max heap property via a type parameter.
  • Extensive tests (including fuzz tests).
  • Benchmarks confirm O(1) push and O(log n) pop.

What makes this heap implementation different?

Unlike other generic Go heaps that I've seen, the ordering function is obtained from an interface implementation rather than a constructor argument. This has advantages and disadvantages.

Advantages:

  • Default values are valid (empty) heaps.
  • All heaps of Ts are guaranteed to use the same function to compare Ts.
  • Empty heaps consume only the space required by a nil slice (as it's not necessary to store the ordering function as a field of the Heap struct).

Disadvantages:

  • The types are more complex (though you don't really have to think about these as a consumer of the library).
  • You need to define dummy wrapper types if you want different heaps to use different ordering functions for the same underlying type.

Example with a built-in type that can be compared using <

package main

import (
	"fmt"

	"github.com/addrummond/heap"
)

func main() {
	var h heap.Heap[int, heap.Max]

	heap.Push(&h, 5)
	heap.Push(&h, 10)
	heap.Push(&h, 1)

	maxVal, ok := heap.Pop(&h)
	// ok == true
	// maxVal == 10
	fmt.Printf("%v: %+v\n", ok, maxVal)
}

Example with a custom data type

package main

import (
	"fmt"

	"github.com/addrummond/heap"
)

type Task struct {
	Priority int
	Payload  any
}

// As Task is a user-defined datatype that doesn't satisfy constraints.Ordered,
// we need to implement the heap.Orderable interface, which has a single method,
// Cmp.
func (t1 *Task) Cmp(t2 *Task) int {
	return t1.Priority - t2.Priority
}

func main() {
	var h heap.Heap[Task, heap.Max]

	heap.PushOrderable(&h, Task{
		Priority: 5,
		Payload:  "A priority 5 task",
	})

	heap.PushOrderable(&h, Task{
		Priority: 10,
		Payload:  "A priority 10 task",
	})

	heap.PushOrderable(&h, Task{
		Priority: 1,
		Payload:  "A priority 1 task",
	})

	maxPriorityTask, ok := heap.PopOrderable(&h)
	// ok == true
	// maxPriorityTask == Task{Priority: 10, Payload: "A priority 10 task"}
	fmt.Printf("%v: %+v\n", ok, maxPriorityTask)
}

Documentation

Overview

Package heap provides a generic min or max heap that can be used as a priority queue. The backing data structure is a slice denoting an implicit binary heap. The default value of Heap is a valid empty heap.

The heap grows in the usual way by appending elements to the backing slice and then making adjustments to preserve the heap property. As the heap shrinks, smaller backing slices are periodically allocated and elements copied over. Empty heaps are guaranteed to be backed by nil slices.

If your type can be compared using the < operator then you can use the Push and Pop functions to manipulate the heap, e.g.:

var myMaxIntHeap heap.Heap[int, heap.Max]
heap.Push(&myMaxIntHeap, 17)
heap.Pop(&myMaxIntHeap)

If your type can't be compared using < then you can implement the Cmp method of the Orderable interface for your type (with a pointer receiver) and use the PushOrderable and PopOrderable functions:

var heap heap.Heap[myCustomType, heap.Min]
heap.PushOrderable(&heap, myCustomType{Key: 1, Foo: "foo"})
heap.PopOrderable(&heap)

type myCustomType struct {
  Key int
  Foo string
}

func (a *myCustomType) Cmp(b *myCustomType) int {
  return x.Key - y.Key
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Clear

func Clear[T any, MOM MinOrMax](heap *Heap[T, MOM])

Clear empties the heap.

func Filter

func Filter[T c.Ordered, MOM MinOrMax](heap *Heap[T, MOM], f func(*T) (keepElement bool, breakOrContinue BreakOrContinue))

Filter iterates through the elements of the heap in the order given by the underlying slice. If the first return value of f is false then the relevant element is removed from the heap. If the second return value of f is Break then the iteration stops without visiting any subsequent items.

func FilterOrderable

func FilterOrderable[T any, MOM MinOrMax, PT Orderable[T]](heap *Heap[T, MOM], f func(*T) (keepElement bool, breakOrContinue BreakOrContinue))

As for Filter, but for the case where T cannot be compared using < and there is an implementation of Orderable[T].

func FromSlice added in v1.3.0

func FromSlice[T c.Ordered, MOM MinOrMax](heap *Heap[T, MOM], slice []T)

FromSlice initializes the a heap from the elements of a slice using Floyd's heap-building algorithm. The previous contents of the heap (if any) are discarded. The slice is 'moved' into the Heap and should not be accessed or modified following a call to this function.

func FromSliceOrderable added in v1.3.0

func FromSliceOrderable[T any, MOM MinOrMax, PT Orderable[T]](heap *Heap[T, MOM], slice []T)

As for FromSlice, but for the case where T cannot be compared using < and there is an implementation of Orderable[T].

func Len

func Len[T any, MOM MinOrMax](heap *Heap[T, MOM]) int

Len returns the number of elements in the heap.

func Peek

func Peek[T any, MOM MinOrMax](heap *Heap[T, MOM]) (val T, ok bool)

Peek returns the min/max element from the min/max heap without removing it.

func Pop

func Pop[T c.Ordered, MOM MinOrMax](heap *Heap[T, MOM]) (T, bool)

Pop removes the min/max element from the heap for a T that satisfies constraints.Ordered.

func PopOrderable

func PopOrderable[T any, MOM MinOrMax, PT Orderable[T]](heap *Heap[T, MOM]) (T, bool)

Pop removes the min/max element from the heap for a T that implements Orderable.

func Push

func Push[T c.Ordered, MOM MinOrMax](heap *Heap[T, MOM], elem T)

Push adds an element to the heap for a T that satisfies constraints.Ordered.

func PushOrderable

func PushOrderable[T any, MOM MinOrMax, PT Orderable[T]](heap *Heap[T, MOM], elem T)

PushOrderable adds an element to the heap for a T that implements Orderable.

Types

type BreakOrContinue

type BreakOrContinue int

A BreakOrContinue value can be returned by an iteration callback to indicate whether or not iteration should continue.

const (
	Break    BreakOrContinue = iota
	Continue BreakOrContinue = iota
)

type Heap

type Heap[T any, MOM MinOrMax] struct {
	nocopy.NoCopy
	// contains filtered or unexported fields
}

Heap is a min or max heap backed by a slice denoting an implicit binary heap. Heap is marked as noCopy because the built-in copying operation creates a shallow copy of the underlying slice, which is likely to give rise to confusing and undesired behavior.

func Copy

func Copy[T any, MOM MinOrMax](heap *Heap[T, MOM]) Heap[T, MOM]

Copy performs a deep copy of the heap

type Max

type Max struct{}

Pass this type as the second parameter of Heap to specify a max heap

type Min

type Min struct{}

Pass this type as the second parameter of Heap to specify a min heap

type MinOrMax

type MinOrMax interface {
	// contains filtered or unexported methods
}

The MinOrMax interface has two implementations (Min and Max) that can be passed as type parameters to Heap to choose between a min and max heap.

type Orderable

type Orderable[R any] interface {
	Cmp(*R) int
	*R
}

If your type doesn't satisfy constraints.Ordered, define a Cmp method with a pointer receiver for your type. This method should return 0 if the two values compare equal, an int < 0 if the first value is less than the second, and an int > 0 otherwise.

Example:

type Date struct {
  Year  int
  Month int
  Day   int
}

func (m1 *Month) Cmp(m2 *Month) int {
  if m1.Year != m2.Year {
    return m1.Year - m2.Year
  }
  if m1.Month != m2.Month {
    return m1.Month - m2.Month
  }
  return m1.Day - m2.Day
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL