Binary heap
A binary heap is a heap data structure that takes the form of a binary tree. Binary heaps are a common way of implementing priority queues. The binary heap was introduced by J. W. J. Williams in 1964, as a data structure for heapsort.
A binary heap is defined as a binary tree with two additional constraints:
- Shape property: a binary heap is a complete binary tree; that is, all levels of the tree, except possibly the last one are fully filled, and, if the last level of the tree is not complete, the nodes of that level are filled from left to right.
- Heap property: the key stored in each node is either greater than or equal to or less than or equal to the keys in the node's children, according to some total order.
Heap operations
Both the insert and remove operations modify the heap to conform to the shape property first, by adding or removing from the end of the heap. Then the heap property is restored by traversing up or down the heap. Both operations take O time.Insert
To add an element to a heap we must perform an up-heap operation, by following this algorithm:- Add the element to the bottom level of the heap at the most left.
- Compare the added element with its parent; if they are in the correct order, stop.
- If not, swap the element with its parent and return to the previous step.
As an example of binary heap insertion, say we have a max-heap
and we want to add the number 15 to the heap. We first place the 15 in the position marked by the X. However, the heap property is violated since, so we need to swap the 15 and the 8. So, we have the heap looking as follows after the first swap:
However the heap property is still violated since, so we need to swap again:
which is a valid max-heap. There is no need to check the left child after this final step: at the start, the max-heap was valid, meaning ; if, and, then, because of the transitive relation.
Extract
The procedure for deleting the root from the heap and restoring the properties is called down-heap.- Replace the root of the heap with the last element on the last level.
- Compare the new root with its children; if they are in the correct order, stop.
- If not, swap the element with one of its children and return to the previous step.
We remove the 11 and replace it with the 4.
Now the heap property is violated since 8 is greater than 4. In this case, swapping the two elements, 4 and 8, is enough to restore the heap property and we need not swap elements further:
The downward-moving node is swapped with the larger of its children in a max-heap, until it satisfies the heap property in its new position. This functionality is achieved by the Max-Heapify function as defined below in pseudocode for an array-backed heap A of length heap_length. Note that "A" is indexed starting at 1.
Max-Heapify :
left ← 2×i '
right ← 2×i + 1
largest ← i
if left ≤ heap_length and A > A then:
largest ← left
if right ≤ heap_length and A > A then:
largest ← right
if largest ≠ i then:
swap A and A
Max-Heapify'
For the above algorithm to correctly re-heapify the array, the node at index i and its two direct children must violate the heap property. If they do not, the algorithm will fall through with no change to the array. The down-heap operation can also be used to modify the value of the root, even when an element is not being deleted. In the pseudocode above, what starts with // is a comment. Note that A is an array that starts being indexed from 1 up to length'', according to the pseudocode.
In the worst case, the new root has to be swapped with its child on each level until it reaches the bottom level of the heap, meaning that the delete operation has a time complexity relative to the height of the tree, or O.
Building a heap
Building a heap from an array of input elements can be done by starting with an empty heap, then successively inserting each element. This approach, called Williams’ method after the inventor of binary heaps, is easily seen to run in time: it performs insertions at cost each.However, Williams’ method is suboptimal. A faster method starts by arbitrarily putting the elements on a binary tree, respecting the shape property. Then starting from the lowest level and moving upwards, shift the root of each subtree downward as in the deletion algorithm until the heap property is restored. More specifically if all the subtrees starting at some height have already been “heapified”, the trees at height can be heapified by sending their root down along the path of maximum valued children when building a max-heap, or minimum valued children when building a min-heap. This process takes operations per node. In this method most of the heapification takes place in the lower levels. Since the height of the heap is, the number of nodes at height is. Therefore, the cost of heapifying all subtrees is:
This uses the fact that the given infinite series converges.
The exact value of the above is known to be equal to:
where is the sum of all digits of the binary representation of and is the exponent of in the prime factorization of.
The average case is more complex to analyze, but it can be shown to asymptotically approach comparisons.
The Build-Max-Heap function that follows, converts an array A which stores a complete
binary tree with n nodes to a max-heap by repeatedly using Max-Heapify in a bottom up manner.
It is based on the observation that the array elements indexed by
,,..., n
are all leaves for the tree, thus each is a one-element heap. Build-Max-Heap runs
Max-Heapify on each of the remaining tree nodes.
Build-Max-Heap :
heap_length ← length
for each index i from floor downto 1 do:
Max-Heapify
Heap implementation
Heaps are commonly implemented with an array. Any binary tree can be stored in an array, but because a binary heap is always a complete binary tree, it can be stored compactly. No space is required for pointers; instead, the parent and children of each node can be found by arithmetic on array indices. These properties make this heap implementation a simple example of an implicit data structure or Ahnentafel list. Details depend on the root position, which in turn may depend on constraints of a programming language used for implementation, or programmer preference. Specifically, sometimes the root is placed at index 1, in order to simplify arithmetic.Let n be the number of elements in the heap and i be an arbitrary valid index of the array storing the heap. If the tree root is at index 0, with valid indices 0 through n − 1, then each element a at index i has
- children at indices 2i + 1 and 2i + 2
- its parent at index floor.
- children at indices 2i and 2i +1
- its parent at index floor.
The upheap/downheap operations can then be stated in terms of an array as follows: suppose that the heap property holds for the indices b, b+1,..., e. The sift-down function extends the heap property to b−1, b, b+1,..., e.
Only index i = b−1 can violate the heap property.
Let j be the index of the largest child of a within the range b,..., e.
By swapping the values a and a the heap property for position i is established.
At this point, the only problem is that the heap property might not hold for index j.
The sift-down function is applied tail-recursively to index j until the heap property is established for all elements.
The sift-down function is fast. In each step it only needs two comparisons and one swap. The index value where it is working doubles in each iteration, so that at most log2 e steps are required.
For big heaps and using virtual memory, storing elements in an array according to the above scheme is inefficient: every level is in a different page. B-heaps are binary heaps that keep subtrees in a single page, reducing the number of pages accessed by up to a factor of ten.
The operation of merging two binary heaps takes Θ for equal-sized heaps. The best you can do is simply concatenating the two heap arrays and build a heap of the result. A heap on n elements can be merged with a heap on k elements using O key comparisons, or, in case of a pointer-based implementation, in O time. An algorithm for splitting a heap on n elements into two heaps on k and n-k elements, respectively, based on a new view
of heaps as an ordered collections of subheaps was presented in. The algorithm requires O comparisons. The view also presents a new and conceptually simple algorithm for merging heaps. When merging is a common task, a different heap implementation is recommended, such as binomial heaps, which can be merged in O.
Additionally, a binary heap can be implemented with a traditional binary tree data structure, but there is an issue with finding the adjacent element on the last level on the binary heap when adding an element. This element can be determined algorithmically or by adding extra data to the nodes, called "threading" the tree—instead of merely storing references to the children, we store the inorder successor of the node as well.
It is possible to modify the heap structure to allow extraction of both the smallest and largest element in Big O notation| time. To do this, the rows alternate between min heap and max heap. The algorithms are roughly the same, but, in each step, one must consider the alternating rows with alternating comparisons. The performance is roughly the same as a normal single direction heap. This idea can be generalised to a min-max-median heap.
Derivation of index equations
In an array-based heap, the children and parent of a node can be located via simple arithmetic on the node's index. This section derives the relevant equations for heaps with their root at index 0, with additional notes on heaps with their root at index 1.To avoid confusion, we'll define the level of a node as its distance from the root, such that the root itself occupies level 0.
Child nodes
For a general node located at index , we will first derive the index of its right child,.Let node be located in level, and note that any level contains exactly nodes. Furthermore, there are exactly nodes contained in the layers up to and including layer . Because the root is stored at 0, the th node will be stored at index. Putting these observations together yields the following expression for the index of the last node in layer l.
Let there be nodes after node in layer L, such that
Each of these nodes must have exactly 2 children, so there must be nodes separating 's right child from the end of its layer.
As required.
Noting that the left child of any node is always 1 place before its right child, we get.
If the root is located at index 1 instead of 0, the last node in each level is instead at index. Using this throughout yields and for heaps with their root at 1.
Parent node
Every node is either the left or right child of its parent, so we know that either of the following is true.Now consider the expression.
If node is a left child, this gives the result immediately, however, it also gives the correct result if node is a right child. In this case, must be even, and hence must be odd.
Therefore, irrespective of whether a node is a left or right child, its parent can be found by the expression:
Related structures
Since the ordering of siblings in a heap is not specified by the heap property, a single node's two children can be freely interchanged unless doing so violates the shape property. Note, however, that in the common array-based heap, simply swapping the children might also necessitate moving the children's sub-tree nodes to retain the heap property.The binary heap is a special case of the d-ary heap in which d = 2.