Chapter 6: Priority Queues (Heaps) Priority Queue ADT Heap Implementation CS 340 Page 100 Heap Applications Leftist Heaps
CS 340 Page 101 Often, a FIFO queue structure has a need for a prioritization capability, so elements with greater priority are removed before lower-priority elements that were actually inserted first. Examples: Short print jobs may be prioritized over longer print jobs on a printer queue. Real-time network applications (e.g., video, audio) may be prioritized over e- mail and simple file transfers on a network switch’s forwarding queue. System maintenance tasks (e.g., memory defragmentation, mouse interrupts) may be prioritized over application software tasks on an operating system’s job queue. Priority Queues
CS 340 Page 102 A heap is a complete binary tree in which every node’s value is less than or equal to all of its offsprings’ values. One Priority Queue Implementation: The Heap Note:One convenient aspect of the heap is that it can be stored in an array Offspring of node i: nodes 2i+1 and 2i+2 Parent of node i: node (i - 1) / 2
CS 340 Page 103 When inserting a new element into a heap, start at the new leaf node that would maintain the binary tree’s completeness, and “percolate” the new element up to its appropriate position to maintain the heap order property. Inserting Into A Heap Insert Percolate Up
CS 340 Page 104 When deleting the minimum element from a heap, create a “hole” node at the root (where the minimum element was), and slide the smaller of the hole’s offspring up until an appropriate slot is found for the last element. Deleting From A Heap Delete Min Percolate Down
CS 340 Page 105 Rather than going to the expense of implementing a complicated system, sometimes it is possible to simulate the system using a statistical model, and to work out the obvious bugs prior to actual implementation. A heap makes a convenient structure in such simulations, where the heap nodes represent the discrete “events” of the system, ordered according to the time at which they “occur”. Example Application: Discrete Event Simulation Network Simulation Example
CS 340 Page 106 Network Simulation Example (Continued) 045: PC1B xmits on TR1 053: ATMS1 xmits on FB 072: PC1B xmits on TR1 068: PC2F xmits on TR2 049: ATMS2 recvs on TR2 080: PC1D xmits on TR1 059: PC2B recvs on TR2 049: ATMS2 recvs on TR2 053: ATMS1 xmits on FB 072: PC1B xmits on TR1 068: PC2F xmits on TR2 059: PC2B recvs on TR2 080: PC1D xmits on TR1 Delete Minimum Process Event 047: ATMS1 recvs on TR1 053: ATMS1 xmits on FB 072: PC1B xmits on TR1 068: PC2F xmits on TR2 049: ATMS2 recvs on TR2 080: PC1D xmits on TR1 059: PC2B recvs on TR2 Delete Minimum 049: ATMS2 recvs on TR2 053: ATMS1 xmits on FB 072: PC1B xmits on TR1 068: PC2F xmits on TR2 059: PC2B recvs on TR2 080: PC1D xmits on TR1 Process Event 049: ATMS2 recvs on TR2 053: ATMS1 xmits on FB 072: PC1B xmits on TR1 068: PC2F xmits on TR2 050: ATMS1 xmits on FB 080: PC1D xmits on TR1 059: PC2B recvs on TR2 Delete Minimum...
CS 340 Page 107 One weakness of the heap structure is the difficulty with which two heaps are merged into one. Merging Heaps How would you merge the two heaps above into a single heap, maintaining the completeness of the binary tree and the heap order property? When might it be necessary to merge two heaps? One printer goes down, and its print jobs are redirected to a second printer with its own priority queue. One printer goes down, and its print jobs are redirected to a second printer with its own priority queue. One network route becomes too congested so a switch must merge the forwarding queues for two of its outgoing lines. One network route becomes too congested so a switch must merge the forwarding queues for two of its outgoing lines. The operating system for a multiprocessor system decides to devote one processor to a certain task, merging its job queue with that of another processor. The operating system for a multiprocessor system decides to devote one processor to a certain task, merging its job queue with that of another processor.
CS 340 Page 108 One solution to the priority queue merging problem is the leftist heap. For any node X in a binary tree, define nullPathLength(X) to be the length of the shortest path from X to a descendant node without two children. A leftist heap is a binary tree with the heap order property (i.e., every node’s value is less than or equal to its offsprings’ values), as well as the leftist heap property: the null path length of each node’s left child is greater than or equal to the null path length of its right child. Like the heap, the leftist heap performs insertions and removals in O(logn) time, but the leftist heap also performs merges in O(logn) time, a big improvement over the heap’s O(n) merge time. Examples: Leftist Heaps
CS 340 Page 109 template leftNode * leftistHeap :: Merge(leftNode *h1, leftNode *h2) { if (h1 == NULL) return h2; if (h2 == NULL) return h1; if (h2->element > h1->element) return Merge1(h1, h2); return Merge1(h2, h1); } template leftNode * leftistHeap :: Merge1(leftNode *h1, leftNode *h2) { if (h1->left == NULL) h1->left = h2; else { h1->right = Merge(h1->right, h2); if (h1->left->nullPathLength right->nullPathLength) Swap(h1->left, h1->right); h1->nullPathLength = h1->right->nullPathLength + 1; } return h1; } template leftNode * leftistHeap :: Merge(leftNode *h1, leftNode *h2) { if (h1 == NULL) return h2; if (h2 == NULL) return h1; if (h2->element > h1->element) return Merge1(h1, h2); return Merge1(h2, h1); } template leftNode * leftistHeap :: Merge1(leftNode *h1, leftNode *h2) { if (h1->left == NULL) h1->left = h2; else { h1->right = Merge(h1->right, h2); if (h1->left->nullPathLength right->nullPathLength) Swap(h1->left, h1->right); h1->nullPathLength = h1->right->nullPathLength + 1; } return h1; } Merging Leftist Heaps
CS 340 Page 110 Leftist Heap Merging Example Original Merge Call st Recursive Call nd Recursive Call rd Recursive Call Final Swap
CS 340 Page 111 STL Priority Queues The Standard Template Library includes a template class for priority queues, priority_queue, implemented as a maximum heap. #include using namespace std; void main( ) { // The first priority_queue uses the // default vector base container priority_queue q1; q1.push( 87 ); q1.push( 65 ); q1.push( 43 ); q1.push( 21 ); cout << "q1 = ( "; while ( !q1.empty( ) ) { cout << q1.top( ) << " "; q1.pop( ); } cout << ")" << endl; // The second priority_queue uses the vector // base container, but specifies that the comparison // function greater be used for ordering elements, // i.e., that the priority queue be a min-heap. priority_queue, greater > q2; q2.push( 87 ); q2.push( 65 ); q2.push( 43 ); q2.push( 21 ); cout << "q2 = ( "; while ( !q2.empty( ) ) { cout << q2.top( ) << " "; q2.pop( ); } cout << ")" << endl; } #include using namespace std; void main( ) { // The first priority_queue uses the // default vector base container priority_queue q1; q1.push( 87 ); q1.push( 65 ); q1.push( 43 ); q1.push( 21 ); cout << "q1 = ( "; while ( !q1.empty( ) ) { cout << q1.top( ) << " "; q1.pop( ); } cout << ")" << endl; // The second priority_queue uses the vector // base container, but specifies that the comparison // function greater be used for ordering elements, // i.e., that the priority queue be a min-heap. priority_queue, greater > q2; q2.push( 87 ); q2.push( 65 ); q2.push( 43 ); q2.push( 21 ); cout << "q2 = ( "; while ( !q2.empty( ) ) { cout << q2.top( ) << " "; q2.pop( ); } cout << ")" << endl; }