C++ Classes and Data Structures Jeffrey S. Childs Chapter 12 Priority Queues, Trees, and Heaps Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall
Priority Queue ADT The data in a priority queue is (conceptually) a queue of elements The “queue” can be thought of as sorted with the largest in front, and the smallest at the end Its physical form, however, may differ from this conceptual view considerably
Priority Queue ADT Operations enqueue, an operation to add an element to the queue dequeue, an operation to take the largest element from the queue an operation to determine whether or not the queue is empty an operation to empty out the queue
Another Priority Queue ADT A priority queue might also be designed to dequeue the element with the minimum value Priority queues are generally not designed to arbitrarily dequeue both minimum and maximum values, whichever the client wants at any particular time We will only consider priority queues that dequeue maximum values (can be easily modified for dequeuing minimum values)
Objects as Elements Elements can be objects in priority queues – for example, Employee objects One would overload relational operators in the Employee struct to determine what data member is being considered for maximum value The client may be interested in dequeuing the employee with the maximum age each time Or, the overloaded operator can be written to dequeue the employee with the maximum salary each time
Priority Queue Implementation To implement a priority queue, an array sorted in descending order comes to mind Dequeuing from a sorted array is easy – just get the value at the current front and increment a front index – this is ( 1 ) time However, enqueuing into a sorted array would take some time – the element would have to be inserted into its proper position in the array…
Enqueuing an Element … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 In this array of elements, each element might be an object, but only the data member considered for maximum value is shown.
Enqueuing an Element (cont.) … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 Suppose that element 71 needs to be enqueued.
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 Suppose that element 71 needs to be enqueued.
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 Suppose that element 71 needs to be enqueued. We could enqueue 71 by using a loop.
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 5 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 67 7 7 5 0 1 2 48 49 50 51 52 98 99 100 i This process continues and i eventually becomes 51 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i This process continues and i eventually becomes 51 for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 69 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i FALSE for ( i = 100; i > 0 && arr[ i - 1 ] < item; i-- ) arr[ i ] = arr[ i – 1 ];
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 70 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i Now we can use: arr[ i ] = item; to enqueue the element
Enqueuing an Element (cont.) item: 71 … … 211 204 201 81 79 71 70 69 20 7 5 0 1 2 48 49 50 51 52 98 99 100 i Now we can use: arr[ i ] = item; to enqueue the element
Enqueuing an Element (cont.) If we assume that, on average, half the elements in an array need to be shifted to insert an element, then the enqueue for an array is a ( n ) algorithm In summary, when using an array for a priority queue: dequeue is ( 1 ) enqueue (on average) is ( n )
Using a Heap to Implement a Priority Queue An alternative to using a sorted array for a priority queue is to use a heap Here, heap does not mean an area of memory used for dynamic allocation – rather, it is a data structure Enqueue in a heap is a O( lg n ) operation Dequeue in a heap is a O( lg n ) operation
Comparing Operations So which is better, the heap or the array? We often eliminate a data structure that has a high time complexity in a commonly used operation, even if the other operations have very low time complexities In the array, on average, an enqueue-dequeue pair of operations takes ( n ) + ( 1 ) time, but ( 1 ) is absorbed into ( n ), leaving us with an overall time complexity of ( n ) per pair of operations
Comparing Operations (cont.) In the heap, each enqueue-dequeue pair of operations takes O( lg n ) + O( lg n ) time, giving us an overall time complexity of O( lg n ) per pair of operations The heap is usually better, although the array can be good in situations where a group of initial elements are supplied and sorted, then only dequeue operations are performed (no enqueue operations)
Trees A heap is a type of tree In order to understand heaps, we need to discuss trees first We will first limit the discussion to the linked list implementation of trees Trees can be defined with paths A path exists from node A to node B if one can follow a chain of pointers to travel from node A to node B
Paths D F A G E B C A set of linked nodes
Paths (cont.) D F A G E B C There is one path from A to B
Paths (cont.) D F A G E B C There is no path from C to B
Paths (cont.) D F A G E B C There is a path from D to B
Paths (cont.) D F A G E B There is also a second path from D to B. C
Paths (cont.) D F A G E B There is also a second path from D to B. C
Definition of Tree A tree is a set of linked nodes, such that there is one and only one path from a unique node (called the root node) to every other node in the tree
Example of a Tree root A C B D G E F
Cycles There is no cycle (circle of pointers) in a tree Any linked structure that has a cycle would have more than one path from the root node to another node
Example of a Cycle D A B C E
Tree Cannot Have a Cycle D A B Node A cannot be a root node of a tree, because there is more than one path from A to C C E
Tree Cannot Have a Cycle (cont.) D A B C One path from A to C E
Tree Cannot Have a Cycle (cont.) D A B C Another path from A to C (passes through C once) E
Example of a Tree In a tree, every pair of linked nodes have a parent-child relationship (the parent is closer to the root) root A C B D G E F
Example of a Tree (cont.) For example, C is a parent of G root A C B D G E F
Example of a Tree (cont.) root A E and F are children of D C B D G E F
Example of a Tree (cont.) The root node is the only node that has no parent. root A C B D G E F
Example of a Tree (cont.) Leaf nodes (or leaves for short) have no children. root A C B D G E F
Subtrees A subtree is a part of a tree that is a tree in itself root A C I K D E F J G H subtree
Subtrees (cont.) It normally includes each node reachable from its root. root A B C I K D E F J G H subtree
Subtrees (cont.) Even though this looks like a subtree in itself… root D E F J G H
Subtrees (cont.) G and H are reachable from the root. root A B C I K D J G H
Subtrees (cont.) So, normally, G and H would be a part of the subtree. root A B C I K D E F J G H
Binary Trees A binary tree is a tree in which each node can only have up to two children…
Not a Binary Tree root A B C I K D E F J G H
Example of a Binary Tree root A B C I K E J G H
Example of a Binary Tree (cont.) The links in a tree are often called edges root A B C I K E J G H
Levels root level 0 A level 1 B C level 2 I K E level 3 J G H The level of a node is the number of edges in the path from the root node to this node
Full Binary Tree root A B C D E F G H I J K L M N O In a full binary tree, each node has two children except for the nodes on the last level, which are leaf nodes
Complete Binary Trees A complete binary tree is a binary tree that is either a full binary tree OR a tree that would be a full binary tree but it is missing the rightmost nodes on the last level
Complete Binary Trees (cont.) root A B C D E F G H I
Complete Binary Trees (cont.) root A B C D E F G H I J K L
Complete Binary Trees (cont.) A full binary tree is also a complete binary tree. root A B C D E F G H I J K L M N O
Heaps A heap is a complete binary tree in which the value of each node is greater than or equal to the values of its children (if any) Technically, this is called a maxheap In a minheap, the value of each node is less than or equal to the values of its children A maxheap can be easily modified to make a minheap In our discussion, the word “heap” will refer to a maxheap
Heaps (cont.) The element stored in each node of a heap can be an object When we talk about the value of a node, we are really talking about some data member of the object that we want to prioritize For example, in employee objects, we may want to prioritize age or salary
Example of a Heap root 46 39 28 16 32 24 2 Only the data member is shown that we want to prioritize 14 3 29
Example of a Heap (cont.) root 46 39 28 16 32 24 2 Where is the greatest value in a heap always found? 14 3 29
Example of a Heap (cont.) root 46 39 28 16 32 24 2 Where is the greatest value in a heap always found? 14 3 29
Dequeue Dequeuing the object with the greatest value appears to be a ( 1 ) operation However, after removing the object, we must turn the resultant structure into a heap again, for the next dequeue Fortunately, it only takes O( lg n ) time to turn the structure back into a heap again (which is why dequeue in a heap is a O( lg n ) operation
Dequeue (cont.) root 46 39 28 16 32 24 2 14 3 29 15 5
Dequeue (cont.) root 46 39 28 16 32 24 2 Save the root object in remElement 14 3 29 15 5
Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 Save the root object in remElement 14 3 29 15 5
Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 14 3 29 15 5
Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 Copy object in last node into root object 14 3 29 15 5
Dequeue (cont.) root remElement: 46 46 39 28 16 32 24 2 Copy object in last node into root object 14 3 29 15 5
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 Copy object in last node into root object 14 3 29 15 5
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 14 3 29 15 5
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 Remove last node 14 3 29 15 5
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 Remove last node 14 3 29 15
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 14 3 29 15
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 5 Greatest Child 39 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 5 Greatest Child 39 39 > 5, so swap 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 5 39 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 Notice that 39 is correctly placed – it is guaranteed to be greater than or equal to its two children 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 It was the greatest child, so it is greater than the other child (28) 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 It is greater than the element it was swapped with (5), or else we wouldn’t have swapped. 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 At this point, we repeat the process, using 5 as the parent. 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 Greatest Child 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 32 > 5, so swap 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 5 28 16 32 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 28 5 32 16 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 28 32 5 16 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 14 3 29 15
Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15 Greatest Child
Dequeue (cont.) root remElement: 46 39 32 28 29 > 5, so swap 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15 Greatest Child
Dequeue (cont.) root remElement: 46 39 32 28 16 5 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 29 15
Dequeue (cont.) root remElement: 46 39 32 28 16 24 2 5 29 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 15
Dequeue (cont.) root remElement: 46 39 32 28 16 24 2 29 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 5 14 3 15
Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 If the value of the greatest child of 5 is greater than 5, then swap with the greatest child 14 3 5 15
Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 14 3 5 15
Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 The final result is a heap! 14 3 5 15
Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 Sometimes, it is not necessary to swap all the way down through the heap 14 3 5 15
Dequeue (cont.) root remElement: 46 39 32 28 16 29 24 2 If 5 would have been greater than or equal to both of its children, we would stop there 14 3 5 15
Heapify The process of swapping downwards to form a new heap is called heapifying When, we heapify, it is important that the rest of the structure is a heap, except for the root node that we are starting off with; otherwise, a new heap won’t be formed A loop is used for heapifying; the number of times through the loop is always lg n or less, which gives the O( lg n ) complexity Each time we swap downwards, the number of nodes we can travel to is reduced by approximately half
Enqueue root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Place the value to enqueue in the last node 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Place the value to enqueue in the last node 14 3 5 15 37
Enqueue (cont.) root 39 32 28 16 29 24 2 14 3 5 15 37
Enqueue (cont.) root 39 32 28 16 29 24 2 If 37 is larger than its parent, swap 14 3 5 15 37
Enqueue (cont.) root 39 32 28 37 > 24, so swap 16 29 24 2 If 37 is larger than its parent, swap 14 3 5 15 37
Enqueue (cont.) root 39 32 28 16 29 24 2 14 3 5 15 37
Enqueue (cont.) root 39 32 28 16 29 2 24 37 14 3 5 15
Enqueue (cont.) root 39 32 28 16 29 2 37 24 14 3 5 15
Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24
Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24
Enqueue (cont.) root 39 32 28 16 29 37 2 If 37 is larger than its parent, swap 14 3 5 15 24
Enqueue (cont.) root 39 37 > 28, so swap 32 28 16 29 37 2 If 37 is larger than its parent, swap 14 3 5 15 24
Enqueue (cont.) root 39 32 28 16 29 37 2 14 3 5 15 24
Enqueue (cont.) root 39 32 28 37 16 29 2 14 3 5 15 24
Enqueue (cont.) root 39 32 28 37 16 29 2 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 Notice that 37 is guaranteed to be greater than or equal to its children 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 It was greater than the value swapped with (28), or we wouldn’t have swapped… 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 and 28 was greater than the other node (2) or it wouldn’t have been a heap… 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 so 37 must be greater than the other node (2) as well. 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 If 37 is larger than its parent, swap 14 3 5 15 24
Enqueue (cont.) root 39 37 < 39, so don’t swap 32 37 16 29 28 2 If 37 is larger than its parent, swap 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 The result is a heap! 14 3 5 15 24
Enqueue (cont.) root 39 32 37 16 29 28 2 Enqueue uses a loop, and it is a O( lg n ) operation (swapping in reverse) 14 3 5 15 24
Implementing a Heap Although it is helpful to think of a heap as a linked structure when visualizing the enqueue and dequeue operations, it is often implemented with an array Let’s number the nodes of a heap, starting with 0, going top to bottom and left to right…
Implementing a Heap (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 11 9 15 16
Heap Properties root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (left child #) = 2 * (parent #) + 1 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (right child #) = 2 * (parent #) + 2 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16
Heap Properties (cont.) root 46 39 28 1 2 16 32 24 25 3 4 5 6 14 3 29 15 5 7 18 17 7 8 9 10 11 12 13 14 (parent #) = (child # - 1) / 2 (using integer division) 11 9 15 16
Array Implementation These remarkable properties of the heap allow us to place the elements into an array The red numbers on the previous slide correspond to the array indexes
Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 So now, this is our heap. It has no linked nodes, so it is much easier to work with. Let’s dequeue an element. The highest value is stored in the root node (index 0).
Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46
Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Now we need to move the object at the last node to the root node We need to keep track of the object in the last position of the heap using a heapsize variable remElement: 46
Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Now we need to move the object at the last node to the root node We need to keep track of the object in the last position of the heap using a heapsize variable remElement: 46 heapsize: 17
Array Implementation (cont.) 46 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Now we can access the object in the last node using elements[ heapsize – 1 ] and assign it to elements[ 0 ] heapsize: 17
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Now we can access the object in the last node using elements[ heapsize – 1 ] and assign it to elements[ 0 ] heapsize: 17
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Next, decrement the heap size heapsize: 17
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 Next, decrement the heap size heapsize: 16
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 The value at index 16 can’t be used anymore; it will be overwritten on the next enqueue remElement: 46 heapsize: 16
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Now, we need to find the greatest child of node 0 and compare it to 9. But how do we get the greatest child? remElement: 46 heapsize: 16
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 By using the formulas we noted earlier (this is why an array can be used)… heapsize: 16
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16
Array Implementation (cont.) Greatest Child 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16
Array Implementation (cont.) Greatest Child 39 > 9, so swap 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 0 + 1 = 1 (right child #) = 2*(parent #) + 2 = 2 * 0 + 2 = 2 heapsize: 16
Array Implementation (cont.) 9 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16
Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16
Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16
Array Implementation (cont.) Greatest Child 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16
Array Implementation (cont.) Greatest Child 32 > 9, so swap 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 1 + 1 = 3 (right child #) = 2*(parent #) + 2 = 2 * 1 + 2 = 4 heapsize: 16
Array Implementation (cont.) 39 9 28 16 32 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 9 32 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 32 9 39 28 16 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16
Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16
Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16
Array Implementation (cont.) Greatest Child 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16
Array Implementation (cont.) Greatest Child 29 > 9, so swap 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 4 + 1 = 9 (right child #) = 2*(parent #) + 2 = 2 * 4 + 2 = 10 heapsize: 16
Array Implementation (cont.) 39 32 28 16 9 24 25 14 3 29 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 9 29 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 29 9 39 32 28 16 24 25 14 3 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 heapsize: 16
Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 If the greatest child of 9 is greater than 9, then swap heapsize: 16
Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 9 + 1 = 19 heapsize: 16
Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 9 + 1 = 19 heapsize: 16 19 > heapsize
Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 so 9 must be a leaf node (we can stop) remElement: 46 (left child #) = 2*(parent #) + 1 = 2 * 9 + 1 = 19 heapsize: 16
Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 An enqueue is done by placing the new element at elements[ heapsize ], then swapping upwards heapsize: 16
Array Implementation (cont.) 39 32 28 16 29 24 25 14 3 9 15 5 7 18 17 11 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 When enqueuing, the parent is always found by using the parent formula: (parent #) = (child # - 1 ) / 2 heapsize: 16
Reducing the Work in a Swap A swap (using simple assignments) cannot just involve two statements: elements[ i ] = elements[ j ]; elements[ j ] = elements[ i ]; On the first line, the value in elements[ i ] is lost; both elements will have the value in elements[ j ]
Reducing the Work in a Swap (cont.) A swap (using simple assignments) would normally involve three statements: temp = elements[ i ]; elements[ i ] = elements[ j ]; elements[ j ] = temp;
Reducing the Work in a Swap (cont.) In an enqueue or a dequeue for a heap, we do not really have to use this three-assignment swap (although it helped to visualize how the enqueue and dequeue process worked) We can save the value we are swapping upwards or downwards Let’s look at an enqueue…
Enqueue root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 Create a new node in the last position 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 We just “pretend” that 37 is here 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 37 > 24, so we pretend to swap (we just copy 24) 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 24 14 3 5 15
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 We now “pretend” 37 has been placed here 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 and we compare 37 to 28 to see if we should “swap” again 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 37 > 28, so we do a one-assignment swap again 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 24 2 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 28 16 29 24 2 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 We “pretend” 37 is here 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 and compare 37 to 39 to see if we should “swap” again 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 This time we shouldn’t swap… 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 but we have one final assignment 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 28 16 29 28 2 but we have one final assignment 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 37 16 29 28 2 but we have one final assignment 14 3 5 15 24
Enqueue (cont.) root value to enqueue: 37 39 32 37 16 29 28 2 (we can stop pretending) 14 3 5 15 24
Dequeue root 39 32 37 16 29 28 2 The same technique can be used when swapping downward 14 3 5 15 24
Parent-Child Formulas Parent-child formulas can also be sped up: (left child #) = (parent #) << 1 + 1 (right child #) = (left child #) + 1 when finding the greatest child, the left and right children are always found together (parent #) = (child # - 1) >> 1 using the shift operator is the same as integer division
PriorityQueue.h 1 #include "Array.h" 2 3 template <class DataType> 4 class PriorityQueue 5 { 6 public: 7 PriorityQueue( ); 8 PriorityQueue( const Array<DataType> & arr ); 9 void enqueue( const DataType & newElement ); 10 bool dequeue( DataType & remElement ); 11 bool isEmpty( ) const; 12 void makeEmpty( );
PriorityQueue.h (cont.) 14 private: 15 Array<DataType> elements; 16 int heapsize; 17 inline void heapify( int i ); 18 }; 19 20 #include "PriorityQueue.cpp"
Two Constructors There are two constructors in PriorityQueue.h The first constructor just initializes an empty heap…
First Constructor 1 template <class DataType> 2 PriorityQueue<DataType>::PriorityQueue( ) 3 : elements( 2 ), heapsize( 0 ) 4 { 5 }
Forming an Initial Heap The second constructor makes a heap out of an initial array A heap of size n can be made by enqueuing n elements one at a time – but it would take O( n lg n ) time (even though n starts off as small) A faster method can be used to make a heap out of an initial array in ( n ) time The heap will be shown as a linked tree, because it is easier to visualize how the method works – but in actuality, when we use the method, we use it on the array
Forming an Initial Heap (cont.) 6 31 5 34 34 11 7 7 12 39 38 5 32 1 34 27 16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Suppose the initial array looks like this. It is not initially a heap, but by rearranging the elements, it will be. We will look at the tree form of this to see why the method works.
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 Drawing it this way, we can easily see that the initial array is not a heap 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 We realize that nodes 8 through 16 are subheaps of only one node. 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 We want to heapify starting with the parent of the last leaf node. 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 To find the last leaf node, we use heapsize – 1 (17 – 1 = 16) 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 Then the parent of the last node is (16 – 1) / 2 = 7 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 Heapifying works only if the structure is a heap except for possibly the root. 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 7 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 27 16 This is true for the structure rooted at index 7, so we can use heapify on it… 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 27 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 7 16 This is true for the structure rooted at index 7, so we can use heapify on it… 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 27 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 7 16 Then we decrement index 7 and use heapify again… 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 7 3 4 5 6 27 12 39 38 5 32 1 34 7 8 9 10 11 12 13 14 7 16 Then we decrement index 7 and use heapify again… 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 34 3 4 5 6 27 12 39 38 5 32 1 7 7 8 9 10 11 12 13 14 7 16 Then we decrement index 7 and use heapify again… 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 34 3 4 5 6 27 12 39 38 5 32 1 7 7 8 9 10 11 12 13 14 7 16 This process continues until we heapify at the root 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 11 34 3 4 5 6 27 12 39 38 5 32 1 7 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 32 34 3 4 5 6 27 12 39 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 34 32 34 3 4 5 6 27 12 39 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 already a heap 15 16
Forming an Initial Heap (cont.) root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) temp root 6 31 5 1 2 34 39 32 34 3 4 5 6 27 12 34 38 5 11 1 7 7 8 9 10 11 12 13 14 7 16 one-assignment swaps 15 16
Forming an Initial Heap (cont.) root 6 31 34 1 2 34 39 32 7 3 4 5 6 27 12 34 38 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 31 34 1 2 34 39 32 7 3 4 5 6 27 12 34 38 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 31 34 1 2 34 39 32 7 3 4 5 6 27 12 34 38 5 11 1 5 7 8 9 10 11 12 13 14 7 16 temp 15 16
Forming an Initial Heap (cont.) root 6 39 34 1 2 34 38 32 7 3 4 5 6 27 12 34 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 39 34 1 2 34 38 32 7 3 4 5 6 27 12 34 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 6 39 34 1 2 34 38 32 7 3 4 5 6 27 12 34 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 temp 15 16
Forming an Initial Heap (cont.) root 39 38 34 1 2 34 34 32 7 3 4 5 6 27 12 6 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 15 16
Forming an Initial Heap (cont.) root 39 38 34 1 2 34 34 32 7 3 4 5 6 27 12 6 31 5 11 1 5 7 8 9 10 11 12 13 14 7 16 The result is a heap 15 16
2nd Constructor 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 }
2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } ((heapsize – 1) – 1)
2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } ((heapsize – 1) – 1) index of last node
2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } index of parent of last node
2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } makes a heap out of the array
2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } finds the least power of 2 that is greater than or equal to the array size
2nd Constructor (cont.) 6 template <class DataType> 7 PriorityQueue<DataType>::PriorityQueue( 8 const Array<DataType> & arr ) 9 : elements( arr ), heapsize( arr.length( ) ) 10 { 11 for ( int i = ( heapsize - 2 ) >> 1; i >= 0; i-- ) 12 heapify( i ); 13 14 int tryPower = 2; 15 for ( ; tryPower < elements.length( ); tryPower <<= 1 ); 16 17 if ( tryPower != elements.length( ) ) 18 elements.changeSize( tryPower ); 19 } if the array size is not already a power of 2, change it to a power of 2 (helps with maintenance)
Enqueue 20 template <class DataType> 21 void PriorityQueue<DataType>::enqueue( 22 const DataType & newElement ) 23 { 24 if ( heapsize == elements.length( ) ) 25 elements.changeSize( elements.length( ) << 1 ); 26 enqueue continued…
Enqueue (cont.) i will be used as the current position 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) heapsize – 1 is the index of the last position – we start i at the next position beyond the last position (where we start the enqueue) 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) We are pretending that we placed newElement in this position (newElement is passed in from the client) 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) This is the parent of the current position. 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) We compare newElement with the parent value… 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) and do a one-assignment swap if newElement is greater 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) Then, the current position becomes the parent of the current position 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) Afterwards, we compare newElement with the parent of the new current position 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) When the loop stops, i is either 0 (the root)… 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) or newElement (which we pretend is at the current position i) is less than or equal to its parent 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) Either way, we found the place to put newElement 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) Then, since we enqueued, heapsize is increased by one 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Enqueue (cont.) Notice that this is an overloaded operator for elements that are objects. 27 int i = heapsize; 28 for ( ; (i != 0) && newElement > elements[ (i - 1) >> 1 ]; 29 i = ( i - 1 ) >> 1 ) 30 elements[ i ] = elements[ ( i - 1 ) >> 1 ]; 31 32 elements[ i ] = newElement; 33 heapsize++; 34 }
Dequeue 35 template <class DataType> 36 bool PriorityQueue<DataType>::dequeue( 37 DataType & remElement ) 38 { 39 if ( !heapsize ) 40 return false; 41 remElement = elements[ 0 ]; 42 heapsize--; 43 elements[ 0 ] = elements[ heapsize ]; 44 heapify( 0 ); dequeue continued…
Dequeue (cont.) 45 int trysize = elements.length( ); 46 while ( ( heapsize <= trysize >> 2 ) && trysize > 2 ) 47 trysize >>= 1; 48 49 if ( trysize < elements.length( ) ) { 50 try { 51 elements.changeSize( trysize ); 52 } 53 catch ( ... ) {} 54 } 55 56 return true; 57 }
isEmpty 58 template <class DataType> 59 bool PriorityQueue<DataType>::isEmpty( ) const 60 { 61 return !heapsize; 62 }
makeEmpty 63 template <class DataType> 64 void PriorityQueue<DataType>::makeEmpty( ) 65 { 66 heapsize = 0; 67 try { 68 elements.changeSize( 2 ); 69 } 70 catch( ... ) { } 71 }
Heapify 72 template <class DataType> 73 inline void PriorityQueue<DataType>::heapify( int i ) 74 { 75 int leftChild, rightChild, largest; 76 bool stop = false; 77 78 DataType temp = elements[ i ]; 79 80 leftChild = (i << 1) + 1; heapify continued…
Heapify (cont.) 81 while ( leftChild < heapsize && !stop ) { 82 rightChild = leftChild + 1; 83 largest = ( rightChild == heapsize )? leftChild : 84 (( elements[leftChild] > elements[rightChild])? 85 leftChild : rightChild ); 86 if ( elements[ largest ] > temp ) { 87 elements[ i ] = elements[ largest ]; 88 i = largest; 89 leftChild = (i << 1) + 1; 90 } 91 else 92 stop = true; 93 } 94 elements[ i ] = temp; 95 }
Heapify (cont.) 81 while ( leftChild < heapsize && !stop ) { 82 rightChild = leftChild + 1; 83 largest = ( rightChild == heapsize )? leftChild : 84 (( elements[leftChild] > elements[rightChild])? 85 leftChild : rightChild ); 86 if ( elements[ largest ] > temp ) { 87 elements[ i ] = elements[ largest ]; 88 i = largest; 89 leftChild = (i << 1) + 1; 90 } 91 else 92 stop = true; 93 } 94 elements[ i ] = temp; 95 } We are careful that the client does not need to overload more than one operator for the class
Linked Heap If we have a large element size, we might want to consider conserving memory with a linked heap should have the same time complexities as the array-based heap
Dequeue Requirements left and right pointers in a node, so we can get the children for swapping a pointer to the last node in the heap, which needs to be maintained (updated) in ( 1 ) time
Enqueue Requirements a parent pointer in a node, for swapping upwards if necessary a ( 1 ) method of finding the place to put the initial node
List through the Heap We work with the end of the array when enqueuing or dequeuing We can work with the end of a linked list instead
List Through the Heap (cont.) shown in red A B C D E F G H I J K L M N O
Embedded Heap This isn’t a heap now, because it isn’t even a tree we have cycles We can say there is an “embedded heap” in this data structure
Required Pointers We’ll need to maintain a pointer at the end of the list called “last”, so we know where we’re supposed to form a new node (for the enqueue) and remove a node (for the dequeue) We’ll also need a pointer to the last parent, so we know which parent to attach a new node to
Adding New Nodes A lastParent B C D E last
Adding New Nodes (cont.) lastParent B C D E F last
Adding New Nodes (cont.) lastParent B C D E F last
Adding New Nodes (cont.) lastParent B C D E F last
Adding New Nodes (cont.) lastParent B C D E F G last
Adding New Nodes (cont.) lastParent B C D E F G last
Adding New Nodes (cont.) lastParent B C D E F G last
Adding New Nodes (cont.) lastParent B C D E F G lastParent = lastParent->next last
Adding New Nodes (cont.) lastParent B C D E F G lastParent = lastParent->next last
Adding New Nodes (cont.) lastParent B C D E F G last
Adding New Nodes (cont.) lastParent B C D E F G last H
Adding New Nodes (cont.) lastParent B C D E F G H last
Adding New Nodes (cont.) lastParent B C D E F G H last
Removing Nodes When we remove a node, we’ll have to bring the last pointer back one node If the list through the heap is a singly-linked list, it might not be easy to find the previous node to bring the “last” pointer back to…
Removing Nodes (cont.) A B C lastParent D E F G node to remove H I J K
Removing Nodes (cont.) If the list through the heap is doubly-linked, it’s easy to find the node to set “last” to…
Removing Nodes (cont.) A B C lastParent D E F G node to remove H I J K
Removing Nodes (cont.) Using the doubly-linked list, we can also bring lastParent back one node when we need to
Node Struct We now have five pointers in a node: template <class DataType> struct PQNode { DataType info; PQNode<DataType> *left; PQNode<DataType> *right; PQNode<DataType> *parent; PQNode<DataType> *back; PQNode<DataType> *next; };
Time Considerations Every time we need to add or remove a node, we’ll need to set a number of pointers In the array-based heap, however, we’d be copying a couple of elements on the average enqueue or dequeue (the average for array expansion / contraction) For large element sizes, it is still worth it
Memory Considerations If an element size is 20 bytes, 50% of the memory in the linked (embedded) heap is wasted If an element size is 1980 bytes, 1% of the memory in the linked (embedded) heap is wasted For large element sizes, it is a worthwhile data structure to consider
Avoiding Special Cases We can avoid handling special cases involving the root by using a “root header node” similar to the header node in the linked list The actual root node will branch off as a single child of the root header Should the actual root branch from the left or right side of the root header?
Position of Actual Root The root pointer, last pointer, and lastParent pointer will start off pointing to the root header We move the lastParent pointer forward after a right child has been attached to its node Therefore, the actual root should branch off the right side of the root header…
Starting it Off last root lastParent root header an empty heap – heap is empty when root == last
Starting it Off (cont.) last root lastParent root header an element is enqueued – “actual root” is inserted A
Starting it Off (cont.) last root lastParent root header A
Starting it Off (cont.) root lastParent root header last A
Starting it Off (cont.) root lastParent root header last A Since a node has been inserted to the right of the lastParent node, it is time to move the lastParent
Starting it Off (cont.) root root header last lastParent A Since a node has been inserted to the right of the lastParent node, it is time to move the lastParent
Starting it Off (cont.) root root header last lastParent A Now, we are in a position to insert to the left and right of this node
Starting it Off (cont.) root root header last lastParent A B
Starting it Off (cont.) root root header lastParent A last B
Starting it Off (cont.) root root header lastParent A last B
Starting it Off (cont.) root root header lastParent A last B C
Starting it Off (cont.) root root header lastParent A last B C
Starting it Off (cont.) root root header lastParent A last B C
Starting it Off (cont.) root root header A last lastParent B C
Starting it Off (cont.) root root header A last lastParent etc. B C
PriorityQueue.h We still need Array.h, since the client will pass an Array into the second constructor 1 #include "Array.h" 2 3 template <class DataType> 4 struct PQNode { 5 DataType info; 6 PQNode<DataType> *left; 7 PQNode<DataType> *right; 8 PQNode<DataType> *parent; 9 PQNode<DataType> *back; 10 PQNode<DataType> *next; 11 }; PriorityQueue.h continued…
PriorityQueue.h (cont.) 12 template <class DataType> 13 class PriorityQueue 14 { 15 public: 16 PriorityQueue( ); 17 PriorityQueue( Array<DataType> & arr ); 18 PriorityQueue( const PriorityQueue<DataType> & appq ); 19 ~PriorityQueue( ); 20 PriorityQueue<DataType> & operator =( 21 const PriorityQueue<DataType> & rpq );
PriorityQueue.h (cont.) 22 void enqueue( const DataType & newElement ); 23 bool dequeue( DataType & deqElement ); 24 bool isEmpty(); 25 void makeEmpty();
PriorityQueue.h (cont.) 26 private: 27 PQNode<DataType> rootNode; 28 PQNode<DataType> *root; 29 PQNode<DataType> *last; 30 PQNode<DataType> *lastParent; 31 bool left; 32 inline void insertNode( const DataType & inf ); 33 inline void heapify( PQNode<DataType> *current ); 34 inline void deepCopy( 35 const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp"
PriorityQueue.h (cont.) Used to determine which side of the lastParent node to insert on… 26 private: 27 PQNode<DataType> rootNode; 28 PQNode<DataType> *root; 29 PQNode<DataType> *last; 30 PQNode<DataType> *lastParent; 31 bool left; 32 inline void insertNode( const DataType & inf ); 33 inline void heapify( PQNode<DataType> *current ); 34 inline void deepCopy( 35 const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp"
PriorityQueue.h (cont.) 26 private: 27 PQNode<DataType> rootNode; 28 PQNode<DataType> *root; 29 PQNode<DataType> *last; 30 PQNode<DataType> *lastParent; 31 bool left; 32 inline void insertNode( const DataType & inf ); 33 inline void heapify( PQNode<DataType> *current ); 34 inline void deepCopy( 35 const PriorityQueue<DataType> & original ); 36 }; 37 38 #include "PriorityQueue.cpp" and also when to move the lastParent pointer
First Constructor 1 template <class DataType> 2 PriorityQueue<DataType>::PriorityQueue( ) 3 { 4 last = lastParent = root = &rootHeader; 5 root->next = NULL; 6 left = false; 7 }
Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 }
Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 } Makes the “embedded tree” (just inserts)
Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 } Makes an embedded heap out of the embedded tree
Second Constructor 8 template <class DataType> 9 PriorityQueue<DataType>::PriorityQueue( 10 Array<DataType> & arr ) 11 { 12 last = lastParent = root = &rootHeader; 13 left = false; 14 15 for ( int i = 0; i < arr.length( ); i++ ) 16 insertNode( arr[ i ] ); 17 18 for ( PQNode<DataType> *current = last->parent; 19 current != root; current = current->back ) 20 heapify( current ); 21 } Remember that root points to the root header
Copy Constructor and Destructor 22 template <class DataType> 23 PriorityQueue<DataType>::PriorityQueue( 24 const PriorityQueue<DataType> & appq ) 25 { 26 deepCopy( appq ); 27 } 28 29 template <class DataType> 30 PriorityQueue<DataType>::~PriorityQueue( ) 31 { 32 makeEmpty( ); 33 }
Overloaded Assignment Operator 34 template <class DataType> 35 PriorityQueue<DataType> & PriorityQueue<DataType>:: 36 operator =( const PriorityQueue<DataType> & rpq ) 37 { 38 if ( this == &rpq ) 39 return *this; 40 makeEmpty( ); 41 deepCopy( rpq ); 42 return *this; 43 }
Enqueue 44 template <class DataType> 45 void PriorityQueue<DataType>::enqueue( 46 const DataType & newElement) 47 { 48 insertNode( newElement ); Places a new node at the end of the heap enqueue continued…
Enqueue (cont.) reheap upwards using one-assignment swaps 49 PQNode<DataType> *current = last, 50 *parent = current->parent; 51 while ( parent != root && newElement > parent->info ) { 52 current->info = parent->info; 53 current = parent; 54 parent = current->parent; 55 } 56 57 current->info = newElement; 58 }
Dequeue 59 template <class DataType> 60 bool PriorityQueue<DataType>::dequeue( 61 DataType & deqElement ) 62 { 63 if ( root == last ) 64 return false; 65 66 PQNode<DataType> *current = root->right; 67 deqElement = current->info; 68 current->info = last->info; dequeue continued…
Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; Code to remove the last node
Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; True if the next node to be inserted will be the left child of the lastParent node…
Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; but we are removing a node, so we have to move lastParent back
Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; We’ll use this pointer convention to show how the code works parent back next left right
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; last lastParent
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last
Dequeue (cont.) root 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; lastParent last
Dequeue (cont.) 69 if (left) { 70 lastParent = lastParent->back; 71 lastParent->right = NULL; 72 } 73 else 74 lastParent->left = NULL; 75 last = last->back; 76 delete last->next; 77 last->next = NULL; dequeue continued…
Dequeue (cont.) toggle left whenever we have to enqueue or dequeue (it was done in the insertNode function for enqueue) 78 left = !left; 79 80 if ( root != last ) 81 heapify( current ); 82 83 return true; 84 }
Dequeue (cont.) current was set to the “actual root” previously 78 left = !left; 79 80 if ( root != last ) 81 heapify( current ); 82 83 return true; 84 }
Dequeue (cont.) if the heap isn’t empty, the last step of dequeue is to heapify 78 left = !left; 79 80 if ( root != last ) 81 heapify( current ); 82 83 return true; 84 }
isEmpty 85 template <class DataType> 86 bool PriorityQueue<DataType>::isEmpty() 87 { 88 return root == last; 89 }
makeEmpty 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } We move back through the doubly-linked list, freeing each last node along the way
makeEmpty (cont.) 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } When done, we want the last and lastParent pointers to point to the root header
makeEmpty (cont.) 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } last points to the root header when the loop stops…
makeEmpty (cont.) 90 template <class DataType> 91 void PriorityQueue<DataType>::makeEmpty() 92 { 93 while ( root != last ) { 94 lastParent = last->back; 95 delete last; 96 last = lastParent; 97 } 98 99 root->next = NULL; 100 left = false; 101 } which means that lastParent also points to the root header
insertNode 102 template <class DataType> 103 inline void PriorityQueue<DataType>::insertNode( 104 const DataType & inf ) 105 { We’ll see how the body of insertNode works on the following slides…
insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root 107 last->next->back = last; lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root 107 last->next->back = last; lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root 108 last = last->next; lastParent last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root 108 last = last->next; last lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 110 last->parent = lastParent; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 110 last->parent = lastParent; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 111 if (left) last lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 113 else { last lastParent 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 114 lastParent->right = last; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 114 lastParent->right = last; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 115 lastParent = lastParent->next; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } last lastParent
insertNode (cont.) root 115 lastParent = lastParent->next; 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root lastParent 117 last->info = inf; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root lastParent 117 last->info = inf; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last inf was passed in as a parameter
insertNode (cont.) root lastParent 118 left = !left; last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
insertNode (cont.) root lastParent last 106 last->next = new PQNode<DataType>(); 107 last->next->back = last; 108 last = last->next; 109 last->left = last->right = last->next = NULL; 110 last->parent = lastParent; 111 if (left) 112 lastParent->left = last; 113 else { 114 lastParent->right = last; 115 lastParent = lastParent->next; 116 } 117 last->info = inf; 118 left = !left; 119 } lastParent last
Heapify 120 template <class DataType> 121 inline void PriorityQueue<DataType>::heapify( 122 PQNode<DataType> *current ) 123 { 124 DataType temp = current->info; 125 126 PQNode<DataType> *leftc = current->left, 127 *rightc = current->right, *largest; 128 largest = (rightc == NULL)? leftc : 129 ((leftc->info > rightc->info)? leftc : rightc ); for one-assignment swaps heapify continued…
Heapify (cont.) 130 while ( (leftc != NULL) && largest->info > temp ) { 131 current->info = largest->info; 132 current = largest; 133 leftc = current->left; 134 rightc = current->right; 135 largest = (rightc == NULL)? leftc : 136 ((leftc->info > rightc->info)? leftc : rightc ); 137 } 138 139 current->info = temp; 140 } the one-assignment swap
Heapify (cont.) 130 while ( (leftc != NULL) && largest->info > temp ) { 131 current->info = largest->info; 132 current = largest; 133 leftc = current->left; 134 rightc = current->right; 135 largest = (rightc == NULL)? leftc : 136 ((leftc->info > rightc->info)? leftc : rightc ); 137 } 138 139 current->info = temp; 140 } largest points to largest child, so move current down to it
Heapify (cont.) 130 while ( (leftc != NULL) && largest->info > temp ) { 131 current->info = largest->info; 132 current = largest; 133 leftc = current->left; 134 rightc = current->right; 135 largest = (rightc == NULL)? leftc : 136 ((leftc->info > rightc->info)? leftc : rightc ); 137 } 138 139 current->info = temp; 140 }
deepCopy 141 template <class DataType> 142 inline void PriorityQueue<DataType>::deepCopy( 143 const PriorityQueue<DataType> & original ) 144 { 145 last = lastParent = root = &rootHeader; 146 root->next = NULL; 147 left = false; 148 149 PQNode<DataType> *originalptr = original.root->next; 150 for ( ; originalptr != NULL; originalptr = originalptr->next ) 153 insertNode( originalptr->info ); 154 }