Skip to content

Commit

Permalink
container/heap: use sort.Interface where possible
Browse files Browse the repository at this point in the history
Functions heap.Pop(), heap.Push() and heap.Remove() use type
heap.Interface. This type is equal to sort.Interface, with Push() and
Pop() methods added. This is necessary, because these functions affect
the size of the resulting heap.

Functions heap.Fix() and heap.Init() are different. They may compare and
swap elements in the underlying container, but never alter its size. It
is therefore possible to just use sort.Interface. This would make these
functions a lot more pleasant to use in situations where you know the
number of elements in the heap is fixed.

Consider the case where you use these functions to merge the results of
n infinitely long monotonically increasing sequences. For example, logs
received from multiple sources that need to be turned into a single
stream of events sorted by time. This can now be implemented as follows:

    type Value ...
    type Sequence <-chan Value

    type PeekedSequence struct {
            First     Value
            Remainder Sequence
    }

    type SequenceHeap []PeekedSequence

    func (h SequenceHeap) Len() int           { return len(h) }
    func (h SequenceHeap) Less(i, j int) bool { return h[i].First < h[j].First }
    func (h SequenceHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

    func MergeSequences(sequences []Sequence, out chan<- Value) {
            h := make(SequenceHeap, 0, len(sequences))
            for _, s := range sequences {
                    h = append(h, PeekedSequence{
                            First:     <-s,
                            Remainder: s,
                    })
            }
            heap.Init(h)
            for {
                    out <- h[0].First
                    h[0].First = <-h[0].Remainder
                    heap.Fix(h, 0)
            }
    }

Before this change was made, SequenceHeap had to provide Pop() and
Push() methods that are known to be unused.
  • Loading branch information
EdSchouten committed Feb 27, 2024
1 parent 7fd62ba commit cff8a71
Showing 1 changed file with 4 additions and 4 deletions.
8 changes: 4 additions & 4 deletions src/container/heap/heap.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type Interface interface {
// Init is idempotent with respect to the heap invariants
// and may be called whenever the heap invariants may have been invalidated.
// The complexity is O(n) where n = h.Len().
func Init(h Interface) {
func Init(h sort.Interface) {
// heapify
n := h.Len()
for i := n/2 - 1; i >= 0; i-- {
Expand Down Expand Up @@ -80,13 +80,13 @@ func Remove(h Interface, i int) any {
// Changing the value of the element at index i and then calling Fix is equivalent to,
// but less expensive than, calling [Remove](h, i) followed by a Push of the new value.
// The complexity is O(log n) where n = h.Len().
func Fix(h Interface, i int) {
func Fix(h sort.Interface, i int) {
if !down(h, i, h.Len()) {
up(h, i)
}
}

func up(h Interface, j int) {
func up(h sort.Interface, j int) {
for {
i := (j - 1) / 2 // parent
if i == j || !h.Less(j, i) {
Expand All @@ -97,7 +97,7 @@ func up(h Interface, j int) {
}
}

func down(h Interface, i0, n int) bool {
func down(h sort.Interface, i0, n int) bool {
i := i0
for {
j1 := 2*i + 1
Expand Down

0 comments on commit cff8a71

Please sign in to comment.