450 likes | 667 Views
Data Structures in C++ Case Study: Priority Queues. Some data structures you have (probably) seen so far: - Linked lists - Hash tables - Trees - Graphs - Stacks - Queues A (regular) queue has first-in, first-out (FIFO) behavior, where
E N D
Data Structures in C++Case Study: Priority Queues Some data structures you have (probably) seen so far: - Linked lists - Hash tables - Trees - Graphs - Stacks - Queues A (regular) queue has first-in, first-out (FIFO) behavior, where items must enter at the rear and leave from the front. In a priority queue items must leave from the front, but they enter on the basis of a priority (key) value. The queue is kept sorted by this value.
Examples of Priority Queues • CPU process queues • Print queues • Event-driven simulation (traffic flows) • VLSI design (channel routing, pin layout) • Artificial intelligence search algorithms
Operations on Priority Queues • Create: Create a new PQ of a given maximum size • Add: Add an element to a PQ based on its priority (key) • Remove: Remove and return the element in a PQ with highest priority • Full: Return whether or not the PQ is full • Empty: Return whether or not the PQ is empty
Implementations of Priority Queues Priority Queue Implemented as: Array Linked List Binary Heap
Array Implementation of Priority Queues Suppose items with priorities 16, 14, 10, 9, 8, 7, 4, 3, 1 are to be stored in a priority queue. One implementation: N Max 1 16 14 10 9 8 7 4 3 1 A Suppose an item with priority 15 is added: Max 1 16 14 10 9 8 7 4 3 1 A Thus inserting takes O(N) time: linear 15
Linked List Implementation of Priority Queues L 16 14 10 9 8 7 4 3 1 Suppose an item with priority 2 is to be added: L 16 14 10 9 8 7 4 3 1 2 Only O(1) (constant) pointer changes required, but it takes O(N) pointer traversals to find the location for insertion. Wanted: a data structure for PQs that can be both searched and updated in better than O(N) time.
Binary Heaps A binary heap is a "nearly full" binary tree (only the bottom level may not be complete): 16 14 10 8 7 9 3 2 4 1 A binary heap has the property that, for every node other than a leaf, its key value is greater or equal to that of its children (called the Heap Property).
Priority Queues as Binary Heaps Consider the Remove operation: 1. Extract the maximum element (root) 2. Replace the root with another element 3. Restore the Heap Property to the result 16 Max 14 10 8 7 9 3 2 4 1
Remove Operation 16 1 Max 14 10 8 7 9 3 2 4 This structure no longer has the Heap Property. So we must "heapify" it. To start, find the larger of the two children of the root, and swap it with the root.
Remove Operation (cont'd) 16 14 Max 1 10 8 7 9 3 2 4 Still does not have the Heap Property, so repeat the process at the selected node.
Remove Operation (cont'd) 16 14 Max 8 10 1 7 9 3 2 4 Still does not have the Heap Property
Remove Operation (cont'd) 16 14 Max 8 10 4 7 9 3 2 1 Does have the Heap Property, so stop.
Efficiency of Remove Q: What property of a binary heap limits the number of times we have to repeat the swapping process? A: Its height. Q: What is the maximum height of a binary heap of N nodes (keeping in mind that it is "nearly full")?
Height of a Binary Heap Suppose: N = number of items in heap h = height of heap A full heap of size 1: N h 1 0
Height of a Binary Heap (cont'd) A full heap of size 3: N h 1 0 3 1
Height of a Binary Heap (cont'd) A full heap of size 7: N h 1 0 3 1 7 2
Height of a Binary Heap (cont'd) A full heap of size 15: N h 1 0 3 1 7 2 15 3 . . . . N ? Q: What is the height of a heap of size N?
Height of a Binary Heap (cont'd) N = 2h+1 - 1 N + 1 = 2h+1 log2(N + 1) = log2(2h+1) = (h + 1)log22 = h + 1 h = log2(N + 1) - 1 But h must be an integer. This formula works only if the tree is full (N = 1, 3, 7, 15, ...). So we take h = floor(log2N) Thus, since the height of a binary heap is floor(log2N), remove occurs in O(logN) time.
The Binary Heap Add Operation Adding to a binary heap must do so in a way which maintains the Heap Property: 1. Grow the heap by 1 (but don't add yet) 2. Shift parents of the newly allocated node down until the correct position is vacated 3. Insert the new node into the position 15 16 new key 14 10 Q: If the new key were inserted here, would the Heap Property be maintained? A: No, so move parent down 8 7 9 3 2 4 1
Add Operation (cont'd) 15 16 new key 14 10 8 9 3 2 4 1 7 Q: If the new key were inserted here, would the Heap Property be maintained? A: No, so repeat
Add Operation (cont'd) 15 16 new key 10 8 14 9 3 2 4 1 7 Q: If the new key were inserted here, would the Heap Property be maintained? A: Yes, so insert
Add Operation (cont'd) 15 16 new key 15 10 8 14 9 3 2 4 1 7 Q: What is the maximum number of parent shifts necessary? A: The height of the binary heap = floor(log2N) So binary heap add can be done in O(logN) time.
Priority Queue Implementations: Efficiency Comparison O(N) array implementation linked list implementation O(logN) binary heap implementation N 0
Efficiency Comparison (cont'd) Suppose there are N=1,000,000 items in a PQ. Q: If the PQ is implemented as an array, what is the maximum number of array cell movements necessary for an insert? A: 1,000,000 (= the number of pointer traversals if PQ is implemented as a linked list) Q: What is the maximum number of parent shifts necessary if PQ is implemented as a binary heap? A: log21,000,000 ~ 20
Implementing a Binary Heap A natural implementation of binary heaps is a tree with pointers, but a more elegant one is through arrays. Priority Queue Implemented as: Array Linked List Binary Heap Implemented as: Tree linked with pointers Array
Binary Heaps As Arrays 16 14 10 This binary heap can be represented by the array: 8 7 9 3 1 2 3 4 5 6 7 8 9 10 16 14 10 8 7 9 3 2 4 1 2 4 1 A Let a node be represented by its array index i. Then the index of the left child of i is 2i, the index of the right child of i is 2i+1, and the index of the parent of i is floor(i/2) --integer divide.
Why The Array Implementation Is Most Efficient • No pointers: • Less memory • No dereferencing • Given a heap item index, its children and parent indexes can be computed very quickly using bit operations
Binary Heap Bit Operations Suppose an item's index is 5. Then the index of its left child is 10 and that of its right child is 11. In binary: 5 0 1 0 1 10 1 0 1 0 Double 11 Double and add 1 1 0 1 1 To compute the index of the parent given the index of either child, do an integer divide by 2.
Using Bit Operations to Double a Value Index of parent The rest of the bits are shifted one position to the left Left-most bit is lost 0 1 0 1 1 0 1 0 Right-most bit gets a zero Index of left child
Adding One When Right-most Bit is Zero 1 0 1 0 Perform a logical or of these 0 0 0 1 To get this result 1 0 1 1 Index of right child
Doing An Integer Divide With Bit Operations Right child Left child Right-most bit is lost 10 1 0 1 0 1 0 1 1 11 Other bits shifted to the right 5 0 1 0 1 0 1 0 1 5 Zero added from left Parent
Priority Queue Operation Design Using a Binary Heap Array Implementation • Create: inherit from QueueInfo class, add a number of items attribute • Full: return whether the number of items is equal to the maximum array size • Empty: return whether the number of items is equal to zero • Remove: for you to design • Add: for you to design
An Opportunity for Code Re-use Queue Item 1 maxSize: Integer front: Integer rear: Integer * items display(): void add(Item): void remove(): Item empty(): Boolean full(): Boolean FrontQueue RearQueue PriorityQueue
Types of Heap Property • Consider the possible Heap Property variations: • For each node other than a leaf, its key value is greater than or equal to those of its children • For each node other than a leaf, its key value is less than or equal to those of its children • Call the first kind of structure a MaxPriorityQueue • Call the second kind a MinPriorityQueue • Note that these are identical except for the key comparison test used by add and remove • So they should inherit everything else from a common parent
More Code Re-use PriorityQueue MaxPriorityQueue MinPriorityQueue