Preface
The standard library container
of golang provides an implementation of heap. This article briefly analyzes how it is implemented and how to use it.
heap
Taking minheap minimal binomial heap as an example, the heap has the following properties.
- Any node is smaller than all its children, and the smallest node is at the root of the heap (heap orderliness)
- The heap is a complete tree: i.e., all nodes in all layers except the bottom layer are filled with elements, and the bottom layer is filled from left to right as much as possible.
- Since a heap is a complete binary tree, it can be represented as an ordered array, as follows
source code analysis
The heap of the standard library is an interface
, so the developer needs to implement the relevant interfaces (5
in total, including 3
of sort.Interface
). During the source code analysis, special attention was paid to the embedding of these public interfaces.
Recall that the minheap insertion/deletion process.
- When inserting
Push
an element into the minheap, insert this element into the last node of the rightmost subtree, and then callup
to adjust upward to ensure the minimum heap property. - When removing the top element (the smallest) from the minheap, swap it with the last node of the right subtree, then
Pop
the last node, then calldown
on the root node to adjust it downward to ensure the minimum heap property. - Fetching data from any position of the minheap is similar to the above.
Main method analysis
-
Init method
Init
is the method to initialize the heap, the key is in theheap.down
method.- In the
Init
method, the position is adjusted so that the first1
element isn/2-1
, in line with the characteristics of minheap; the last position is0
at the top of the heap to ensure that no element is left out. - If
i
is smaller than its children, theni
is swapped with the smaller of the two children (j
in the code); the childj
is then compared with its children, and swapped until the end of the array, or the element to be compared is is smaller than both of its children, the currentheap.down
loop is jumped out.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// A heap must be initialized before any of the heap operations // can be used. Init is idempotent with respect to the heap invariants // and may be called whenever the heap invariants may have been invalidated. // Its complexity is O(n) where n = h.Len(). // func Init(h Interface) { // heapify n := h.Len() for i := n/2 - 1; i >= 0; i-- { down(h, i, n) } } // Given the type, the index of the element to be adjusted in the array and the length of the heap // Sink the element to the appropriate position in the subtree corresponding to the element, so that the subtree is the smallest heap func down(h Interface, i0, n int) bool { i := i0 for { j1 := 2*i + 1 if j1 >= n || j1 < 0 { // j1 < 0 after int overflow break } j := j1 // left child if j2 := j1 + 1; j2 <n && h.Less(j2, j1) { j = j2 // = 2*i + 2 // right child } if !h.Less(j, i) { // The minimum heap requirement has been met, exit break } h.Swap(i, j) i = j } return i > i0 }
- In the
-
Push
methodThe
Push
method ensures that when a new element is inserted, the sequential arrayh
remains a heap; as described above, thex
element is inserted at the end of the array, and then theup
method is called to adjust it from the bottom up to satisfy the heap property.The
heap.up
method is also easy to understand: find the parent node (i
) of the elementj
according to this (for loop
), and ifj
is smaller than the parent nodei
, swap the two nodes and continue comparing to the parent node further up the hierarchy until the root node, or the elementj
is larger than the parent nodei
(the adjustment is done, no need to continue).1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Push pushes the element x onto the heap. The complexity is // O(log(n)) where n = h.Len(). func Push(h Interface, x interface{}) { h.Push(x) up(h, h.Len()-1) } func up(h Interface, j int) { for { i := (j - 1) / 2 // parent if i == j || !h.Less(j, i) { break } h.Swap(i, j) j = i } }
-
Pop
methodThe
heap.Pop
method takes out the data from the top position of the heap (minheap is the minimum), and after taking the data, the heap is definitely not balanced. So the usual practice is to swap the root node (0
position) with the elements of the last node, and adjust the elements of the new root node (the previous last element) down to the appropriate position from top to bottom to satisfy the minheap requirement.Finally, the user-defined Pop function is called to get the last element.
There is a fundamental difference between the Pop method of the heap package and the Pop method of the user-defined implementation, where the user’s
Pop
method is only used to get the data. -
Remove
methodThe
Remove
method provides an implementation to remove the index element at the specified position, i.e. swap the nodei
to be removed with the last noden
, and then sink or float the new nodei
to the appropriate position (in layman’s terms, due to new data adjustments, the original last position has risen to where it should not be, and the element needs to be adjusted, first all the way down to the end, and then all the way up to the final position). -
Fix
methodThe meaning of the
Fix
method is in the priority queue scenario (rebalancing the heap after a change in data from thei
position, the priority queue will use this method).1 2 3 4 5 6 7 8 9
// Fix re-establishes the heap ordering after the element at index i has changed its value. // 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) { if !down(h, i, h.Len()) { up(h, i) } }
heap instantiation
The following implements a heap using the slice structure of []int
, and note that the call is made using the methods of the heap package.
|
|
Usage scenarios for heap
- timer
- priority queue
- heap sorting
Summary
To use the standard library’s heap, you need to specify these items.
- Define your own interface that implements the methods needed by the heap.
- Note that the methods of package, and the methods of structs, although with the same name, have completely different functions.
- The usage scenarios of
heap.Fix
andheap.Remove
methods.