Priority Queues and Heaps - Data Structures | CS 261, Papers of Data Structures and Algorithms

Material Type: Paper; Class: DATA STRUCTURES; Subject: Computer Science; University: Oregon State University; Term: Fall 2006;

Typology: Papers

Pre 2010

Uploaded on 08/31/2009

koofers-user-u2s
koofers-user-u2s 🇺🇸

9 documents

1 / 12

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Chapter P: Priority queues and Heaps
1
Chapter P: Priority Queues and Heaps
In this chapter we examine yet another variation on the simple
Bag data structure. A priority queue maintains values in order
of importance. A metaphor for a priority queue is a to-do list of
tasks waiting to be performed, or a list of patients waiting for an
operating room in a hospital. The key feature is that you want to
be able to quickly find the most important item, the value with
highest priority.
Like a bag, you can add new elements into the priority queue.
However, the only element that can be accessed or removed is
the one value with highest priority. In this sense the container is like the stack or queue,
where it was only the element at the top or the front of the collection that could be
removed.
The Priority Queue ADT specification
The traditional definition of the Priority Queue abstraction includes the following
operations:
Add (newelement)
Add a new value to queue
First ()
Return first element in queue
removeFirst ()
Remove first element in queue
isEmpty()
Return true if collection is empty
Note that a priority queue is not, in the technical sense, a true queue. To be a queue,
elements would need to satisfy the FIFO property. This is clearly not the case for the
priority queue. However, the name is now firmly attached to this abstraction, so it is
unlikely to change.
Applications of Priority Queues
To be written. Simulations are an important application.
Priority Queue Implementation Techniques
We will explore three different queue implementation techniques. The first is to examine
how any sort of ordered bag (such as the SortedBag, SkipList, or AVLtree) can be used to
implement the priority queue. The second approach introduces a new type of binary tree,
termed the heap. The classic heap (known simply as the heap) provides a very memory
efficient representation for a priority queue. Our third technique, the skew heap, uses an
interesting variation on the heap technique.
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

Download Priority Queues and Heaps - Data Structures | CS 261 and more Papers Data Structures and Algorithms in PDF only on Docsity!

Chapter P: Priority Queues and Heaps

In this chapter we examine yet another variation on the simple Bag data structure. A priority queue maintains values in order of importance. A metaphor for a priority queue is a to-do list of tasks waiting to be performed, or a list of patients waiting for an operating room in a hospital. The key feature is that you want to be able to quickly find the most important item, the value with highest priority. Like a bag, you can add new elements into the priority queue. However, the only element that can be accessed or removed is the one value with highest priority. In this sense the container is like the stack or queue, where it was only the element at the top or the front of the collection that could be removed.

The Priority Queue ADT specification

The traditional definition of the Priority Queue abstraction includes the following operations: Add (newelement) Add a new value to queue First () Return first element in queue removeFirst () Remove first element in queue isEmpty() Return true if collection is empty Note that a priority queue is not, in the technical sense, a true queue. To be a queue, elements would need to satisfy the FIFO property. This is clearly not the case for the priority queue. However, the name is now firmly attached to this abstraction, so it is unlikely to change.

Applications of Priority Queues

To be written. Simulations are an important application.

Priority Queue Implementation Techniques

We will explore three different queue implementation techniques. The first is to examine how any sort of ordered bag (such as the SortedBag, SkipList, or AVLtree) can be used to implement the priority queue. The second approach introduces a new type of binary tree, termed the heap. The classic heap (known simply as the heap) provides a very memory efficient representation for a priority queue. Our third technique, the skew heap, uses an interesting variation on the heap technique.

A note regarding the name heap. The term heap is used for two very different concepts in computer science. The heap data structure is an abstract data type used to implement priority queues. The terms heap , heap memory. heap allocation , and so on are used to describe memory that is allocated directly by the user, using the malloc function. You should not confuse the two uses of the same term.

Worksheet H1: Building a Priority Queue using an Ordered Bag

In an ordered collection the smallest element is always the first value. While the bag does not support direct access to the first element (as does, say, the queue), we can nevertheless obtain access to this value by means of an iterator. This makes it very easy to implement a Heap using an ordered bag as a storage mechanism for the underlying data values, as the following example illustrates: struct AVLtree * data = newAVLtree() ; /* already written */ void heapAdd (EleType newElement, struct AVLtree data) { avlTreeAdd(data, newElement); } EleType heapFirst (struct AVLtree * data) { AVLtreeIterator itr = data.iterator(); if (AVLtreeIteratorHasNext(itr)) { return AVLtreeIteratorNext(itr); } else assert(0); / trying to return value from empty queue */ } int heapIsEmpty (struct AVLtree * data) { return AVLtreeSize(data) == 0; } void removeFirst (struct AVLtree * data) { EleType f = heapFirst(data); AVLtreeRemove(f); } We have seen three ordered collections, the SortedArray, the SkipList, and the AVLtree. Based on your knowledge of the algorithmic execution speeds for operations in those data structures, fill in the following table with the execution times for the BagHeap constructed in a fashion similar to the able. Operation SortedArray SkipList AVLtree Add(newElement) First() removeFirst() Notice that in the above scheme we have exposed the underlying AVL tree by forcing the user to explicitly allocate and create the tree in order to use the heap. This minimizes the

Notice that a heap is partially ordered, but not completely. In particular, the smallest element is always at the root. Although we will continue to think of the heap as a tree, we will make use of the fact that a complete binary tree can be very efficiently represented as an array. To root of the tree will be stored as the first element in the array. The children of node i are found at positions 2i+1 and 2i+2, the parent at (i-1)/2. You should examine the tree above, and verify that the transformation given will always lead you to the children of any node. To reverse the process, to move from a node back to the parent, simply subtract 1 and divide by 2. You should also verify that this process works as you would expect. We will construct our heap by defining functions that will use an underlying dynamic array as the data container. This means that users will first need to create a new dynamic array before they can use our heap functions: struct dyArray * heap = newDyArray(10); /* create a new array */ … To insert a new value into a heap the value is first added to the end. (This operation has actually already been written in the function dyArrayAdd). Adding an element to the end preserves the complete binary tree property, but not the heap ordering. To fix the ordering, the new value is percolated up into position. It is compared to its parent node. If smaller, the node and the parent are exchanged. This continues until the root is reached, or the new value finds its correct position. Because this process follows a path in a complete binary tree, it is O(log n). The following illustrates adding the value 4 into a heap, then percolating it up until it reaches its final position. When the value 4 is compared to the 2, the parent node containing the 2 is smaller, and the percolation process halts. Because the process of percolating up traverses a complete binary tree from leaf to root, it is O(log n), where n represents the number of nodes in the tree. Percolating up takes care of insertion into the heap. What about the other operations? The smallest value is always found at the root. This makes accessing the smallest element easy. But what about the removal operation? When the root node is removed it leaves a “hole.” Filling this hole with the last element in the heap restores the complete binary tree

property, but not the heap order property. To restore the heap order the new value must percolate down into position. To percolate down a node is compared to its children. If there are no children, the process halts. Otherwise, the value of the node is compared to the value of the smallest child. If the node is larger, it is swapped with the smallest child, and the process continues with the child. Again, the process is traversing a path from root to leaf in a complete binary tree. It is therefore O(log n). Using the existing functions to manipulate a dynamic array, the process of removing the first element can be described as follows: void removeFirst(struct dyArray heap) { int last = dyArraySize(heap); assert(last != 0); / make sure there is at least one value / / Copy the last element to the first position/ dyArraySet(heap->data, 0, dyArrayrGet(heap, last-1)); dyArrayRemoveAt(heap, last-1); / Remove last element./ adjustHeap(heap, dyArraySize(heap), 0); / Rebuild heap property./ } AdjustHeap can easily be written as a recursive routine: void adjustHeap (struct dyArray hdata, int max, int pos) int leftChild = 2pos + 1; int rightChild = 2 * pos + 2; if (rightChild < max) { / we have two children / get index of smallest child if value at pos < value of smallest child swap with smallest child, call adjustHeap (max, index of smallest child) else if (leftchild < max) { / we have one child / if value at pos < value of child swap with smallest child, call adjustHeap (max, index of child) / else no children, done */

void heapAdd (struct dyArray heap, EleType newValue) { dyArrayAdd(heap, newValue); / add to end of array / / then percolate down into position */ } }

Worksheet H3: Heap Sort

When a value is removed from a heap the size of the heap is reduced. If the heap is being represented in an array, this means that the bottom-most element of the array is no longer being used. (Using the terminology of the dynamic array we examined earlier, the size of the collection is smaller, but the capacity remains unchanged). pictures What if we were to store the removed value in the now unused section of the array? In fact, since the last element in the array is always moved into the hole made by the removal of the root, it is a trivial matter to simply swap this value with the root.

picture Suppose we repeat this process, always swapping the root with the currently last element, then reheaping by percolating the new root down into position. The result would be a sorting algorithm, termed heap sort. The following is a snapshot illustrating heap sort in the middle of execution. Notice that the smallest elements have been moved to the right. The current size of the dynamic array is indicated by the sharp drop in values. The elements to the left of this point are organized in a heap. Notice that the heap is not completely ordered, but has a tendency towards being ordered. To determine the algorithmic execution time for this algorithm, recall that adjustHeap requires O(log n) steps. There are n executions of adjustHeap to produce the initial heap. Afterwards, there are n further executions to reheap values during the process of sorting. Altogether the running time is O(n log n). This matches that of merge sort, quick sort, and tree sort. Better yet, heap sort requires no additional storage. Simulate execution of the Heap sort algorithm on the following values: 9 3 2 4 5 7 8 6 1 0 First make the values into a heap (the graphical representation is probably easier to work with than the vector form). Then repeatedly remove the smallest value, and rebuild the heap.

Worksheet H4: Skew Heaps *

1 (^1) This section can be safely omitted at the discretion of the instructor.

The merge algorithm for a skew heap can be described as follows: Node merge (Node left, Node right) if (left is null) return right if (right is null) return left if (left child value < right child value) { Node temp = left.left; left.left = merge(left.right, right) left.right = temp return left; } else { Node temp = right.right right.right = merge(right.left, left) right.left = temp return right } Complete the implementation of the SkewHeap based on these ideas: class SkewHeap implements PQueue { public SkewHeap (Comparator t) { test = t; } private Compator test; private Node root = null; private static class Node { … } // as with other trees public void add (E val) { root = merge(root, new Node(val)); } public E getFirst () { if (root == null) throw new NoSuchElementException(); return root.value; }

public void removeFirst () { root = merge(root.leftChid, root.rightChild); } private Node merge (Node left, Node right) { } }

Short Exercises

  1. Given an example of a priority queue that occurs in a non-computer science situation.
  2. Why is a priority queue not a true queue in the sense described in Chapter q?
  3. What property characterizes the nodes in a heap?
  4. Where is the smallest value found in a heap?
  5. Where is the 2nd^ smallest element in a heap? The third smallest element?
  6. Show that unless other information is maintained, finding the maximum value in a heap must be an O(n) operation. Hint: How many elements in the heap could potentially be the maximum?
  7. What is the height of a heap that contains 10 elements?
  8. If you interpret a sorted vector as a heap, is it guaranteed to have the heap order property?
  9. Could you implement the tree sort algorithm using a heap? Recall that the tree sort algorithm simply copies values from a vector into a container (that is, the heap), the repeatedly removes the smallest element and copies it back to the vector. What is the big-oh execution time for this algorithm?