350 likes | 468 Views
CS 261 – Data Structures. Priority Queues & Heaps. Priority Queues. Not really a FIFO queue – misnomer!! Associates a “priority” with each object: First element has the highest priority A data structure that supports the PQueue interface: public void add(Object val);
E N D
CS 261 – Data Structures Priority Queues & Heaps
Priority Queues • Not really a FIFO queue – misnomer!! • Associates a “priority” with each object: • First element has the highest priority • A data structure that supports the PQueue interface: public void add(Object val); public Object getFirst(); public void removeFirst(); • Examples of priority queues: • To do list with priorities • Active processes in an OS
Use of Priority Queues in Simulations • The original, and one of the most important uses of Priority queues • Discrete Event Driven Simulation • Actions are represented by “events” - things that have (or will) happen at a given time • The PQ maintains list of pending events. Highest priority is next event. • Event is pulled from list, executed, usually spawns more events, which are then inserted into the PQ. • Loop until everything happens, or until fixed time is reached.
Priority Queue Applications • Discrete event-driven simulation – i.e., a simulation of a process where events occur • Example: Ice cream store • People arrive • People order • People leave • Simulation Algorithm: • Determine the times of each event using a random number generator with some distribution: • Put all events in a priority queue based on when it happens • Simulation framework pulls the minimum (next to happen) and executes the event
Priority Queue Applications • Many types of events … but they all have a time when they occur • Can we store various types of events in a priority queue? • Heterogeneous collection • Keep a pointer to action, or a flag to tell what action to perform • So, have your comparison based on the time
Priority Queues: Implementations We can definitely do better than these!!!
Priority Queues: Heaps • Heap: has 2 completely different meanings • Classic data structure used to implement priority queues • Memory space used for dynamic allocation We will study the data structure (not dynamic memory allocation) • Heap data structure: a complete binary tree in which every node’s value is less than or equal to the values of its children • Review: a complete binary tree is a tree in which • Every node has at most two children (binary) • The tree is entirely filled except for the bottom level which is filled from left to right (complete) • Longest path is ceiling(log n) for n nodes
Heap: Example Root = Smallest element 2 3 5 9 10 7 8 14 12 11 16 Next open spot Last filled position (not necessarily the last object added)
Maintaining the Heap: Addition 2 Add element: 4 3 5 9 10 7 8 New element in next open spot. 4 14 12 11 16 Place new element in next available position, then fix it by “percolating up”
Maintaining the Heap: Addition (cont.) 2 3 5 2 9 10 4 8 3 4 14 12 11 16 7 After first iteration (swapped with 7) 9 10 5 8 14 12 11 16 7 After second iteration (swapped with 5) New value not less than parent Done Percolating up: while new value is less than parent, swap value with parent
Maintaining the Heap: Removal • Since each node’s value is less than or equal to the values of its children, the root is always the smallest element • Thus, the PQueue methods getFirst and removeFirst access and remove the root node, respectively • Heap removal (removeFirst): • Replace root with the element in the last filled position • Fix heap by “percolating down”
Maintaining the Heap: Removal • removeFirst: • Move value in last • element into root • 2. Percolate down Root = Smallest element 2 3 5 9 10 7 8 14 12 11 16 Last filled position
Maintaining the Heap: Removal (cont.) 16 3 5 3 9 10 7 8 16 5 14 12 11 Root object removed (16 copied to root and last node removed) 9 10 7 8 14 12 11 After first iteration (swapped with 3) Percolating down: while greater than smallest child swap with smallest child
Maintaining the Heap: Removal (cont.) 3 9 5 3 16 10 7 8 8 9 5 14 12 11 After second iteration (moved 9 up) 12 10 7 8 14 16 11 After third iteration (moved 12 up) Reached leaf node Stop percolating Percolating down: while greater than smallest child swap with smallest child
Maintaining the Heap: Removal (cont.) Root = New smallest element 3 9 5 12 10 7 8 14 16 11 New last filled position
Heap Representation • Recall that a complete binary tree can be efficiently represented by an array or a vector: • Children of node at index i are stored at indices • Parent of node at index i is at index 2i + 1 and 2i + 2 floor((i - 1) / 2) 2 3 5 9 10 7 8 14 12 11 16 3 9 5 7 7 14 8 12 9 11 10 16 0 2 1 3 2 5 4 10 6 8
Heap Implementation: add (cont.) void addElement(DyArray da, EleType val) { int pos = dyArraySize(da); // Get index of next open spot. dyArrayAdd(da, val); // add element at end int up = (pos – 1) / 2; // Get parent index (up the tree). ... } Example:add 4 to the heap Prior to addition, size = 11 Parent position (up) Next open spot (pos) 3 9 5 7 7 14 8 12 9 11 10 16 11 4 0 2 1 3 2 5 4 10 6 8
A couple of Useful Routines void swap (dyArray * da, int i, in j) { // swap elements at I and j EleType temp = dyArrayGet(da, i); dyArrayPut(da, i, dyArrayGet(da, j)); dyArrayPut(da, j, temp); } int indexSmallest (dyArray * da, int i, int j) { // return index of smallest element if (LT(dyArrayGet(da, i), dyArrayGet(da, j))) return i; return j; }
Heap Implementation: add (cont.) public void add(dyArray * da, EleType val) { ... // While not at root and new value is less than parent. while ((pos != 0) && (indexSmallest(da, pos, up) == pos)) { swap(da, pos, up); // swap values of parent and child pos = up; up = (pos - 1) / 2; // Move position and compute parent idx. ... } After first iteration:“swapped” new value (4) with parent (7) New parent value: 5 up pos 3 9 5 4 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 5 4 10 6 8
Heap Implementation: add (cont.) public void add(dyArray * da, EleType val) { ... // While not at root and new value is less than parent. while ((pos != 0) && (indexSmallest(da, pos, up) == pos)) { swap(da, pos, up); // swap values of parent and child pos = up; up = (pos - 1) / 2; // Move position and compute parent idx. ... } After second iteration:“swapped” new value (4) with parent (5) New parent value: 2 up pos 3 9 5 5 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 4 4 10 6 8
Heap Implementation: add (cont.) public void add(Dyarray * da, EleType val) { ... // While not at root and new value is less than parent. while ((pos != 0) && (indexSmallest(da, pos, up) == pos)) { . . . }// End while: reached root or parent is smaller than new value. } // Done. Second part of while test fails:stop iteration and add new value up pos 3 9 5 5 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 4 4 10 6 8
Heap Implementation: add (cont.) 2 3 5 9 10 7 8 up 4 14 12 11 16 pos 3 9 5 7 7 14 8 12 9 11 10 16 11 4 0 2 1 3 2 5 4 10 6 8
Heap Implementation: add (cont.) 2 3 5 up 9 10 4 8 pos 7 14 12 11 16 3 9 5 4 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 5 4 10 6 8
Heap Implementation: add (cont.) 2 3 4 up 9 10 5 pos 8 7 14 12 11 16 3 9 5 5 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 4 4 10 6 8
Heap Implementation: add (cont.) 2 3 4 9 10 5 pos 8 7 14 12 11 16 3 9 5 5 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 4 4 10 6 8
Heap Implementation: removeFirst void removeFirst(dyArray * da) { int last = dyArraySize(da) – 1; if (last != 0) // Copy the last element to dyArrayPut(da,0, dyArrayGet(da, last)); // the first position. dyArrayRemoveAt(da, last); // Remove last element. adjustHeap(dyArraySize(da), 0); // Rebuild heap property. } First element (elementAt(0)) Last element (last) 3 9 5 5 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 4 4 10 6 8 3 9 5 5 7 14 8 12 9 11 10 16 11 0 7 1 3 2 4 4 10 6 8
Heap Implementation: adjustHeap void adjustHeap(EleType * data, int max, int idx) { What’s next:val>min Copy min to parent spot (idx) Move idx to child child (left child smallest) idx max min = 3 3 9 5 5 7 14 8 12 9 11 10 16 11 0 7 1 3 2 4 4 10 6 8 val = 7
Heap Implementation: adjustHeap (cont.) 2 3 4 9 10 5 8 7 14 12 11 16 last 3 9 5 5 7 14 8 12 9 11 10 16 11 7 0 2 1 3 2 4 4 10 6 8
Heap Implementation: adjustHeap (cont.) 7 3 4 9 10 5 8 New root 14 12 11 16 max 3 9 5 5 7 14 8 12 9 11 10 16 11 0 7 1 3 2 4 4 10 6 8
Heap Implementation: adjustHeap (cont.) 7 val>min Copy min to parent spot (idx) Move idx to child 3 4 val = 7 9 10 5 8 Smallest child (min = 3) 14 12 11 16 max 3 9 5 5 7 14 8 12 9 11 10 16 11 0 7 1 3 2 4 4 10 6 8
Heap Implementation: adjustHeap (cont.) 3 val>min Copy min to parent spot (idx) Move idx to child 7 4 val = 7 9 10 5 8 idx 14 12 11 16 max 3 9 5 5 7 14 8 12 9 11 10 16 11 0 3 1 7 2 4 4 10 6 8
Heap Implementation: adjustHeap (cont.) 3 If larger than both children Done 7 4 val = 7 9 10 5 8 idx 14 12 11 16 max Smallest child (min = 9) 3 9 5 5 7 14 8 12 9 11 10 16 11 0 3 1 7 2 4 4 10 6 8
AdjustHeap - Write recursively void adjustHeap (EleType * data, int max, int pos) { int leftChild = pos * 2 + 1; int rightChild = pos * 2 + 2; if (rightChild < max) { // have two children // get index of smallest // compare smallest child to pos // if necessary, swap and call adjustHeap(max, child) } else if (leftChild < max) { // have only one child // compare child to parent // if necessary, swap and call adjustHeap(max, child) } // else no children, we are at bottom }
Priority Queues: Performance Evaluation So, which is the best implementation of a priority queue?
Priority Queues: Performance Evaluation • Remember that a priority queue’s main purpose is rapidly accessing and removing the smallest element! • Consider a case where you will insert (and ultimately remove)n elements: • ReverseSortedVector and SortedList: Insertions: n * n = n2 Removals: n * 1 = n Total time: n2 + n = O(n2) • Heap: Insertions: n * log n Removals: n * log n Total time: n * log n + n * log n = 2n log n = O(n log n)