The Heap ADT A heap is a complete binary tree where each node’s datum is greater than or equal to the data of all of the nodes in the left and right subtrees. Example 1 17 15 10 9 4 14 7 6 8 11 5 3 2 1 The relational ordering between data in parent / children nodes can be different than >=, for example can be a “less than” ordering. Height of a heap Proposition: The height of a heap H storing n keys is log(n) + 1. Justification: The number of internal nodes in a complete binary tree is at most 1 + 2 + 4 + ... + 2h-1 = 2h - 1 However, if the lowest level is not complete, the number of internal nodes can be as little as 1 + 2 + 4 + ... + 2h-2 + 1 = 2h-1 That is, the number of internal nodes in a heap is 2h-1 <= n <= 2h - 1, which implies that the height, h, is log(n + 1) <= h <= log(n) + 1 Operations on heaps All heap operations are based on the following idea: 1 2. Make a simple structural modification of the heap (different for different operations), which may result in a violation of the heap condition. Traverse the heap from the root to the bottom, or from the bottom to the root (depending on the operation) to recover the heap condition everywhere. The most important operations on heaps are: – insertItem (item) removeItem () replaceItem (item) – deleteItem (item) – – Inserts item Removes the root and returns the datum stored Replaces the root with item unless this violates the heap condition Deletes (arbitrary) item Linear representation of a heap The easiest way to represent a complete binary tree is by means of a onedimensional array of size 2h+1 - 1, where h is the height of the tree. The most attractive property of this representation is that given a position of a node, j, positions of its parent and children can be easily defined (j / 2 is the position of the parent, j * 2 is the position of the left child, and j *2 + 1 is the position of the right child. Our example 1 heap can be represented as follows: k 1 2 3 A[k] 17 15 10 4 5 6 7 8 9 10 11 12 13 14 9 14 8 3 4 7 6 11 5 2 1 Linear representation of a heap (contd.) The insertItem operation in this representation requires the size of the array to be increased by one, and A[size] := newNode 17 15 10 9 4 2 14 7 k 1 3 A[k] 17 15 10 8 6 11 5 3 2 1 13 4 5 6 7 8 9 10 11 12 13 14 15 9 14 8 3 4 7 6 11 5 2 1 13 Now 13 must be walked upheap to fix the heap condition. Its parent is in 15/2, i.e. the 7th position; then, the parent’s parent is in 7/2 position, etc. The upheap method Algorithm upheap (A[n], j, precedes) Input: array A[n] representing the heap, index of the child’s node j, precedes, a comparator object defining the ordering relationship Output: array A[n] with data originally in j walked up until accordingly ordered with its parent (according to the precedes function) int l := j int key := A[l] int k := l/2 while (k >= 1) and precedes(A[k], key) { A[l] := A[k] l := k k := l/2 } A[l] := key The insertItem method Algorithm insertItem (H, Item, precedes) Input: heap H[size], item to be inserted precedes, a comparator object defining the ordering relationship Output: boolean variable success set to true if insertItem is successful if H.full() success := false else { success := true size := size + 1 H[size] := Item upheap(H[size], size, precedes) } return success The efficiency if insertItem operation is O(log n), where n is the number of tree nodes. Linear representation of a heap (contd.) The removeItem operation decreases the size of the array by one and makes A[1] := A[size] 1 15 10 9 4 2 14 7 k 1 3 A[k] 17 15 10 8 6 11 5 3 2 4 5 6 7 8 9 10 11 12 13 14 9 14 8 3 4 7 6 11 5 2 1 Now 1 must be walked downheap to fix the heap condition. Its children are in positions 1*2 and 1*2 + 1. Compare to the larger of the two children and if this child is greater, exchange the two. Continue until the heap condition is fixed. The downheap method Algorithm downheap (A[n], j, precedes) Input: array A[n] representing the heap, index of the parent’s node j, precedes, a comparator object defining the ordering relationship Output: array A[n] with data originally in j walked down until accordingly ordered w.r.t both of its children (according to the precedes function) boolean foundSpot := false int l := j int key := A[l] int k := 2 * l // get the left child first while (k <= n) and (! foundSpot) { if (k < n) and (! precedes (A[k+1], A[k]]) k := k + 1 if (! precedes (A[k], key)) A[l] := A[k], l := k, k := 2 * l else foundSpot := true } // end while A[l] := key The removeItem method Algorithm removeItem (H, precedes) Input: heap H[size], precedes, a comparator object defining the ordering relationship Output: boolean variable success set to true if removeItem is successful if H.empty() success := false else { success := true item := H[1] H[1] := H[size] size := size - 1 downheap(H[size], 1, precedes)} return success The efficiency of removeItem operation is O(log n), where n is the number of tree nodes. Linear representation of a heap (contd.) The replaceItem operation does not change the size of the array but replaces the root node with a new one, and recovers the heap condition, if necessary. 12 15 10 9 4 2 14 7 k 1 3 A[k] 12 15 10 8 6 11 5 3 2 1 4 5 6 7 8 9 10 11 12 13 14 9 14 8 3 4 7 6 11 5 2 1 Now 12 must be walked downheap to fix the heap condition. Linear representation of a heap (contd.) The deleteItem operation decreases the size of the array by one and makes A[item] := A[size]; the heap condition must be fixed after that. Assume we want to remove 15. 17 1 10 9 4 14 7 8 6 11 5 3 2 k 1 2 3 4 5 6 7 8 9 10 11 12 13 A[k] 17 1 10 9 14 8 3 4 7 6 11 5 2 Now 1 must be walked downheap to fix the heap condition.