560 likes | 679 Views
CHAPTER 11: Priority Queues and Heaps. Java Software Structures: Designing and Using Data Structures Third Edition John Lewis & Joseph Chase. Chapter Objectives. Define a heap abstract data structure Demonstrate how a heap can be used to solve problems Examine various heap impmentations
E N D
CHAPTER 11: Priority Queues and Heaps Java Software Structures: Designing and Using Data Structures Third Edition John Lewis & Joseph Chase
Chapter Objectives • Define a heap abstract data structure • Demonstrate how a heap can be used to solve problems • Examine various heap impmentations • Compare heap implementations
Heaps • A heap is a binary tree with two added properties: • It is a complete tree • For each node, the node is less than or equal to both the left child and the right child • This definition describes a minheap
Heaps • In addition to the operations inherited from a binary tree, a heap has the following additional operations: • addElement • removeMin • findMin
The HeapADT /** * HeapADT defines the interface to a Heap. * * @author Dr. Lewis * @author Dr. Chase * @version 1.0, 9/9/2008 */ package jss2; public interface HeapADT<T> extends BinaryTreeADT<T> { /** * Adds the specified object to this heap. * * @param obj the element to added to this head */ public void addElement (T obj);
The HeapADT (continued) /** * Removes element with the lowest value from this heap. * * @return the element with the lowest value from this heap */ public T removeMin(); /** * Returns a reference to the element with the lowest value in * this heap. * * @return a reference to the element with the lowest value in this heap */ public T findMin(); }
The addElement Operation • The addElement method adds a given element to the appropriate location in the heap • The proper location is the one location that will maintain the completeness of the tree
The addElement Operation • There is only one correct location for the insertion of a new node • Either the next open position from the left at level h • Or the first position in level h+1 if level h is full
The addElement Operation • Once we have located the new node in the proper position, then we must account for the ordering property • We simply compare the new node to its parent value and swap the values if necessary • We continue this process up the tree until either the new value is greater than its parent or the new value becomes the root of the heap
The removeMin Operation • The removeMin method removes the minimum element from the heap • The minimum element is always stored at the root • Thus we have to return the root element and replace it with another element
The removeMin Operation • The replacement element is always the last leaf • The last leaf is always the last element at level h
The removeMin Operation • Once the element stored in the last leaf has been moved to the root, the heap will have to reordered • This is accomplished by comparing the new root element to the smaller of its children and swapping them if necessary • This process is repeated down the tree until the element is either in a leaf or is less than both of its children
Using Heaps: Priority Queue • A priority queue is a collection that follows two ordering rules: • Items which have higher priority go first • Items with the same priority use a first in, first out method to determine their ordering • A priority queue could be implemented using a list of queues where each queue represents items of a given priority
Using Heaps: Priority Queue • Another solution is to use a minheap • Sorting the heap by priority accomplishes the first ordering • However, the first in, first out ordering for items with the same priority has to be manipulated
Using Heaps: Priority Queue • The solution is to create a PriorityQueueNode object that stores the element to be placed on the queue, the priority of the element and the arrival order of the element • Then we simply define a compareTo method for the PriorityQueueNode class that first compares priority then arrival time • The PriorityQueue class then extends the Heap class and stores PriorityQueueNodes
The PriorityQueueNode class /** * PriorityQueueNode represents a node in a priority queue containing a * comparable object, order, and a priority value. * * @author Dr. Lewis * @author Dr. Chase * @version 1.0, 8/19/08 */ public class PriorityQueueNode<T> implements Comparable<PriorityQueueNode> { private static int nextorder = 0; private int priority; private int order; private T element; /** * Creates a new PriorityQueueNode with the specified data. * * @param obj the element of the new priority queue node * @param prio the integer priority of the new queue node */
The PriorityQueueNode class (cont.) public PriorityQueueNode (T obj, int prio) { element = obj; priority = prio; order = nextorder; nextorder++; } /** * Returns the element in this node. * * @return the element contained within this node */ public T getElement() { return element; } /** * Returns the priority value for this node. * * @return the integer priority for this node */
The PriorityQueueNode class (cont.) public int getPriority() { return priority; } /** * Returns the order for this node. * * @return the integer order for this node */ public int getOrder() { return order; } /** * Returns a string representation for this node. * */ public String toString() { String temp = (element.toString() + priority + order); return temp; }
The PriorityQueueNode class (cont.) /** * Returns the 1 if the current node has higher priority than * the given node and -1 otherwise. * * @param obj the node to compare to this node * @return the integer result of the comparison of the obj node and this * this one */ public int compareTo(PriorityQueueNode obj) { int result; PriorityQueueNode<T> temp = obj; if (priority > temp.getPriority()) result = 1; else if (priority < temp.getPriority()) result = -1; else if (order > temp.getOrder()) result = 1; else result = -1; return result; } }
The PriorityQueue class /** * PriorityQueue demonstrates a priority queue using a Heap. * * @author Dr. Lewis * @author Dr. Chase * @version 1.0, 8/19/08 */ import jss2.*; public class PriorityQueue<T> extends ArrayHeap<PriorityQueueNode<T>> { /** * Creates an empty priority queue. */ public PriorityQueue() { super(); }
The PriorityQueue class (continued) /** * Adds the given element to this PriorityQueue. * * @param object the element to be added to the priority queue * @param priority the integer priority of the element to be added */ public void addElement (T object, int priority) { PriorityQueueNode<T> node = new PriorityQueueNode<T> (object, priority); super.addElement(node); } /** * Removes the next highest priority element from this priority * queue and returns a reference to it. * * @return a reference to the next highest priority element in this queue */ public T removeNext() { PriorityQueueNode<T> temp = (PriorityQueueNode<T>)super.removeMin(); return temp.getElement(); } }
Implementing Heaps with Links • A linked implementation of a minheap would simply be an extension of our LinkedBinaryTree class • However, since we need each node to have a parent pointer, we will create a HeapNode class to extend our BinaryTreeNode class we used earlier
The HeapNode class /** * HeapNode creates a binary tree node with a parent pointer for use * in heaps. * * @author Dr. Lewis * @author Dr. Chase * @version 1.0, 9/9/2008 */ package jss2; public class HeapNode<T> extends BinaryTreeNode<T> { protected HeapNode<T> parent; /** * Creates a new heap node with the specified data. * * @param obj the data to be contained within the new heap nodes */ HeapNode (T obj) { super(obj); parent = null; } }
Implementing Heaps with Links • The addElement method must accomplish three tasks: • Add the new node at the appropriate location • Reorder the heap • Reset the lastNode pointer to point to the new last node
LinkedHeap /** * Heap implements a heap. * * @author Dr. Lewis * @author Dr. Chase * @version 1.0, 9/9/2008 */ package jss2; import jss2.exceptions.*; public class LinkedHeap<T> extends LinkedBinaryTree<T> implements HeapADT<T> { public HeapNode<T> lastNode; public LinkedHeap() { super(); }
LinkedHeap - addElement /** * Adds the specified element to this heap in the appropriate * position according to its key value. Note that equal elements * are added to the right. * * @param obj the element to be added to this head */ public void addElement (T obj) { HeapNode<T> node = new HeapNode<T>(obj); if (root == null) root=node; else { HeapNode<T> next_parent = getNextParentAdd(); if (next_parent.left == null) next_parent.left = node; else next_parent.right = node; node.parent = next_parent; } lastNode = node; count++; if (count>1) heapifyAdd(); }
LinkedHeap - the addElement Operation • The addElement operation makes use of two private methods: • getNextParentAdd that returns a reference to the node that will be the parent of the new node • heapifyAdd that reorders the heap after the insertion
LinkedHeap - getNextParentAdd /** * Returns the node that will be the parent of the new node * * @return the node that will be a parent of the new node */ private HeapNode<T> getNextParentAdd() { HeapNode<T> result = lastNode; while ((result != root) && (result.parent.left != result)) result = result.parent; if (result != root) if (result.parent.right == null) result = result.parent; else { result = (HeapNode<T>)result.parent.right; while (result.left != null) result = (HeapNode<T>)result.left; } else while (result.left != null) result = (HeapNode<T>)result.left; return result; }
LinkedHeap - heapifyAdd /** * Reorders this heap after adding a node. */ private void heapifyAdd() { T temp; HeapNode<T> next = lastNode; temp = next.element; while ((next != root) && (((Comparable)temp).compareTo (next.parent.element) < 0)) { next.element = next.parent.element; next = next.parent; } next.element = temp; }
LinkedHeap - the removeMin Operation • The removeMin operation must accomplish three tasks: • Replace the element stored in the root with the element stored in the last leaf • Reorder the heap if necessary • Return the original root element
LinkedHeap - removeMin /** * Remove the element with the lowest value in this heap and * returns a reference to it. Throws an EmptyCollectionException * if the heap is empty. * * @return the element with the lowest value in * this heap * @throws EmptyCollectionException if an empty collection exception occurs */ public T removeMin() throws EmptyCollectionException { if (isEmpty()) throw new EmptyCollectionException ("Empty Heap"); T minElement = root.element; if (count == 1) { root = null; lastNode = null; } else {
LinkedHeap – removeMin (cont.) HeapNode<T> next_last = getNewLastNode(); if (lastNode.parent.left == lastNode) lastNode.parent.left = null; else lastNode.parent.right = null; root.element = lastNode.element; lastNode = next_last; heapifyRemove(); } count--; return minElement; }
LinkedHeap - the removeMin Operation • Like the addElement operation, the removeMin operation makes use of two private methods • getNewLastNode that returns a reference to the new last node in the heap • heapifyRemove that reorders the heap after the removal
LinkedHeap – getNewLastNode /** * Returns the node that will be the new last node after a remove. * * @return the node that willbe the new last node after a remove */ private HeapNode<T> getNewLastNode() { HeapNode<T> result = lastNode; while ((result != root) && (result.parent.left == result)) result = result.parent; if (result != root) result = (HeapNode<T>)result.parent.left; while (result.right != null) result = (HeapNode<T>)result.right; return result; }
LinkedHeap – heapifyRemove /** * Reorders this heap after removing the root element. */ private void heapifyRemove() { T temp; HeapNode<T> node = (HeapNode<T>)root; HeapNode<T> left = (HeapNode<T>)node.left; HeapNode<T> right = (HeapNode<T>)node.right; HeapNode<T> next; if ((left == null) && (right == null)) next = null; else if (left == null) next = right; else if (right == null) next = left; else if (((Comparable)left.element).compareTo(right.element) < 0) next = left; else next = right;
LinkedHeap – heapifyRemove (cont.) temp = node.element; while ((next != null) && (((Comparable)next.element).compareTo (temp) < 0)) { node.element = next.element; node = next; left = (HeapNode<T>)node.left; right = (HeapNode<T>)node.right; if ((left == null) && (right == null)) next = null; else if (left == null) next = right; else if (right == null) next = left; else if (((Comparable)left.element).compareTo(right.element) < 0) next = left; else next = right; } node.element = temp; }
Implementing Heaps with Arrays • An array implementation of a heap may provide a simpler alternative • In an array implementation, the location of parent and child can always be calculated • Given that the root is in position 0, then for any given node stored in position n of the array, its left child is in position 2n + 1 and its right child is in position 2(n+1) • This means that its parent is in position (n-1)/2
ArrayHeap /** * ArrayHeap provides an array implementation of a minheap. * * @author Dr. Lewis * @author Dr. Chase * @version 1.0, 9/9/2008 */ package jss2; import jss2.exceptions.*; public class ArrayHeap<T> extends ArrayBinaryTree<T> implements HeapADT<T> { public ArrayHeap() { super(); }
Implementing Heaps with Arrays • Like the linked version, the addElement operation for an array implementation of a heap must accomplish three tasks: • Add the new node, • Reorder the heap, • Increment the count by one • The ArrayHeap version of this method only requires one private method, heapifyAdd which reorders the heap after the insertion
ArrayHeap - addElement /** * Adds the specified element to this heap in the appropriate * position according to its key value. Note that equal elements * are added to the right. * * @param obj the element to be added to this heap */ public void addElement (T obj) { if (count==tree.length) expandCapacity(); tree[count] =obj; count++; if (count>1) heapifyAdd(); }
ArrayHeap - heapifyAdd /** * Reorders this heap to maintain the ordering property after * adding a node. */ private void heapifyAdd() { T temp; int next = count - 1; temp = tree[next]; while ((next != 0) && (((Comparable)temp).compareTo (tree[(next-1)/2]) < 0)) { tree[next] = tree[(next-1)/2]; next = (next-1)/2; } tree[next] = temp; }
ArrayHeap - the removeMin Operation • The removeMin operation must accomplish three tasks: • Replace the element stored at the root with the element stored in the last leaf • Reorder the heap as necessary • Return the original root element • Like the addElement operation, the removeMin operation makes use of a private method, heapifyRemove to reorder the heap
ArrayHeap - removeMin /** * Remove the element with the lowest value in this heap and * returns a reference to it. Throws an EmptyCollectionException if * the heap is empty. * * @return a reference to the element with the * lowest value in this head * @throws EmptyCollectionException if an empty collection exception occurs */ public T removeMin() throws EmptyCollectionException { if (isEmpty()) throw new EmptyCollectionException ("Empty Heap"); T minElement = tree[0]; tree[0] = tree[count-1]; heapifyRemove(); count--; return minElement; }