Heaps are a useful data structure for designing efficient sorting algorithms and priority queues. A heap is a binary tree with the following properties:
It is a complete binary tree.
Each node is greater than or equal to any of its children.
A binary tree is complete if every level of the tree is full, except that the last level may not be full and all the leaves on the last level are placed left-most. For example, in Figure 20.23, the binary trees in (a) and (b) are complete, but the binary trees in (c) and (d) are not complete. Further, the binary tree in (a) is a heap, but the binary tree in (b) is not a heap, because the root (39) is less than its right child (42).
A heap is a binary tree, so it can be represented using a binary tree data structure. However, a more efficient representation for a heap is using an array or an array list if the heap size is known in advance. The heap in Figure 20.24(a) can be represented using an array in Figure 20.24(b). The root is at position 0, and its two children are at positions 1 and 2. For a node at position i , its left child is at position 2 i + 1, its right child is at position 2 i + 2, and its parent is ( i - 1)/2. For example, the node for element 39 is at position 4, so its left child (element 14) is at 9 (2 x 4 + 1), its right child (element 33) is at 10 (2 x 4 + 2), and its parent (element 42) is at 1 ((4 -1)/2.
If the heap size is not known in advance, it is better to use an array list to store a heap.
Often you need to remove the root from a heap. After the root is removed, the tree must be rebuilt to maintain the heap property. The algorithm for building the tree can be described as follows :
Move the last node to replace the root; Let the root be the current node; while (the current node has children and the current node is smaller than one of its children) { Swap the current node with the larger of its children; Now the current node is one level down; }
Figure 20.25 shows the process of rebuilding a heap after the root 62 is removed from Figure 20.24(a). Move the last node 9 to the root, as shown in Figure 20.25(a). Swap 9 with 59 as shown in Figure 20.25(b). Swap 9 with 44 as shown in Figure 20.25(c). Swap 9 with 30 as shown in Figure 20.25(d).
To add a new node to the heap, first add it to the end of the heap and then rebuild the tree as follows:
Let the last node be the current node; while (the current node is greater than its parent) { Swap the current node with its parent; Now the current node is one level up; }
Figure 20.26 shows the process of rebuilding a heap after adding a new node 88 to the heap in Figure 20.25(d). Place the new node 88 at the end of the tree, as shown in Figure 20.26(a). Swap 88 with 13, as shown in Figure 20.26(b). Swap 88 with 44, as shown in Figure 20.26(c). Swap 88 with 59, as shown in Figure 20.26(d).
Now you are ready to design and implement the Heap class. The class diagram is shown in Figure 20.27. Its implementation is given in Listing 20.10.
1 public class Heap { 2 private java.util.ArrayList list = new java.util.ArrayList(); 3 4 /** Create a default heap */ 5 public Heap() { 6 } 7 8 /** Create a heap from an array of objects */ 9 public Heap(Object[] objects) { 10 for ( int i = ; i < objects.length; i++) 11 add(objects[i]); 12 } 13 14 /** Add a new object into the heap */ 15 public void add(Object newObject) { 16 list.add(newObject); // Append to the heap 17 int currentIndex = list.size() - 1 ; // The index of the last node 18 19 while (currentIndex > ) { 20 int parentIndex = (currentIndex - 1 ) / 2 ; 21 // Swap if the current object is greater than its parent 22 if (((Comparable)(list.get(currentIndex))).compareTo( 23 list.get(parentIndex)) > ) { 24 Object temp = list.get(currentIndex); 25 list.set(currentIndex, list.get(parentIndex)); 26 list.set(parentIndex, temp); 27 } 28 else 29 break ; // the tree is a heap now 30 31 currentIndex = parentIndex; 32 } 33 } 34 35 /** Remove the root from the heap */ 36 public Object remove() { 37 if (list.size() == ) return null ; 38 39 Object removedObject = list.get( ); 40 list.set( , list.get(list.size() - 1 )); 41 list.remove(list.size() - 1 ); 42 43 int currentIndex = ; 44 while (currentIndex < list.size()) { 45 int leftChildIndex = 2 * currentIndex + 1 ; 46 int rightChildIndex = 2 * currentIndex + 2 ; 47 48 // Find the maximum between two children 49 if (leftChildIndex >= list.size()) break ; // The tree is a heap 50 int maxIndex = leftChildIndex; 51 if (rightChildIndex < list.size()) { 52 if (((Comparable)(list.get(maxIndex))).compareTo( 53 list.get(rightChildIndex)) < ) { 54 maxIndex = rightChildIndex; 55 } 56 } 57 58 // Swap if the current node is less than the maximum 59 if (((Comparable)(list.get(currentIndex))).compareTo( 60 list.get(maxIndex)) < ) { 61 Object temp = list.get(maxIndex); 62 list.set(maxIndex, list.get(currentIndex)); 63 list.set(currentIndex, temp); 64 currentIndex = maxIndex; 65 } 66 else 67 break ; // The tree is a heap 68 } 69 70 return removedObject; 71 } 72 73 /** Get the number of nodes in the tree */ 74 public int getSize() { 75 return list.size(); 76 } 77 } |
A heap is represented using an array list internally (line 2). You may change it to other data structures, but the Heap class contract will remain unchanged. See Exercise 20.9.
The add(Object newObject) method (lines 15 “33) appends the object to the tree and then swaps it with its parent if it is greater than its parent. This process continues until the new object becomes the root or is not greater than its parent.
The remove() method (lines 36 “71) removes and returns the root. To maintain the heap property, the method moves the last object to the root position and swaps it with its larger child if it is less than the larger child. This process continues until the last object becomes a leaf or is not less than its children.
Listing 20.11 gives an example of using a heap to sort numbers . Line 3 creates a heap from an array of integers. The loop in lines 5 “7 displays all the elements in the heap in decreasing order.
1 public class TestHeap { 2 public static void main(String[] args) { 3 Heap heap = new Heap( new Integer[]{ 8 , 9 , 2 , 3 , 4 , 1 , 5 , 6 , 7 }); 4 5 while (heap.getSize() > ) 6 System.out.print(heap.remove() + " " ); 7 } 8 } |
The printout from Listing 20.11 is
9 8 7 6 5 4 3 2 1