PRIORITY QUEUE CONCEPT A priority queue is best understood in comparison with a stack and a queue. To see this, imagine a supermarket checkout, where customers, each with a certain number of items in the shopping cart, arrive at the checkout counter: ItemsInCart 6 12 4 9 15 7 Customer Mary //last to arrive Joe Jill Pete Stacy Bev //first to arrive CHECKOUT COUNTER Suppose also that the entries <6, Mary>, <12, Joe> … <7, Bev> are placed in a data container, to reflect order of arrival, with <7, Bev> first in, <15, Stacy> next in, and so on, and <6, Mary> in last. If the cashier serves the customers in order of arrival, that is, <7, Bev> first and <6, Mary>last, we have a conventional queue, i.e. First In, First Out, or FIFO. If the cashier serves the customers starting with entries <6, Mary>, and ending with <7, Bev>, we have a stack, i.e. Last In, First Out, or LIFO. If the cashier serves the customers with entries in the order <4, Jill>, <6, Mary>, <7, Bev>, … ending with <15, Stacy>, that is, service in order of lowest number of shopping items, we have a priority queue, i.e. The entry inserted with the lowest priority key, no matter when inserted, is the first entry out. In this example, the first data item of each entry, the number of shopping items, serves as the priority key. Notice the technical term entry. A priority queue consists of a set of entries into the queue, each entry consisting of a priority key and a value. Applications • Scheduling jobs on a workstation holds jobs to be performed and their priorities. When a job is finished or interrupted, highest-priority job is chosen using Extract-Max. New jobs can be added using Insert function. • Operating System Design – resource allocation • Data Compression -Huffman algorithm • Discrete Event simulation (1) Insertion of time-tagged events (time represents a priority of an event -- low time means high priority) (2) Removal of the event with the smallest time tag 18 Implementation • Linked Lists • Using a binary Heap – a special binary tree with heap property We show Front and Rear arrows to provide a comparison with an ordinary queue, but they’re not really necessary. The algorithms know that the front of the queue is always at the top of the array at nItems-1, and they insert items in order, not at the rear. Figure below shows the operation of the PriorityQ class methods. Two items removed from front of priority queue Key Comparison Method. Normally the entry with the highest priority has the lowest priority key value, and is extracted from the priority queue first. That means that we need a way to compare the key values, so that we can say if the key of one entry is greater or less than the key of another entry, and which key has the lowest value and which the highest value. The key comparison method may be very simple, based on integer values, as in the case of number of shopping items above. The Priority Queue ADT A priority queue ADT will be implemented as a container of some kind that can support the methods below. constructor Create a new, empty queue. 19 insert Add a new item to the queue. remove Remove and return an item from the queue. The item that is returned is the one with the highest priority. empty Check whether the queue is empty. Sorting With a Priority Queue. We can look at a priority queue as a black box. You can put entries into it, using insert(), in any key order, take out a few entries, using removeMin(), put in some more and so on, as if using a stack. But no matter how many we put in and take out, removeMin() always delivers the entry in the queue with the lowest key value. This is obviously useful for sorting. Suppose we want to sort a set of entries (each made up of a key and a value) in ascending order: Step 1. Use insert() to insert, in any order, all the entries into the priority queue. Step 2. Use removeMin() to extract the entries from the queue, and print them, or place them in an array. The entries are now sorted. Class PriorityQueue The insert() method checks whether there are any items; if not, it inserts one at index 0. Otherwise, it starts at the top of the array and shifts existing items upward until it finds the place where the new item should go. Then it inserts the item and increments nItems. Note that if there’s any chance the priority queue is full, you should check for this possibility with isFull() before using insert(). The front and rear fields aren’t necessary as they were in the Queue class because, as we noted, front is always at nItems-1 and rear is always at 0. The remove() method is simplicity itself: It decrements nItems and returns the item from the top of the array. The isEmpty() and isFull() methods check if nItems is 0 or maxSize, respectively public void insert(long item) // insert item { int j; if(nItems==0) // if no items, queArray[nItems++] = item; // insert at 0 else // if items, { for(j=nItems-1; j>=0; j--) // start at end, { if( item > queArray[j] ) // if new item larger, queArray[j+1] = queArray[j]; // shift upward else // if smaller, 20 break; // done shifting } // end for queArray[j+1] = item; // insert it nItems++; } // end else (nItems > 0) } // end insert() //------------------------------------------------------------public long remove() // remove minimum item { return queArray[--nItems]; } Linked Lists A linked list, in its simplest form, is a collection of nodes that together form a linear ordering. Linked Lists are a very common way of storing arrays of data. The major benefit of linked lists is that you do not specify a fixed size for your list. The more elements you add to the chain, the bigger the chain gets. There is more than one type of a linked list, we'll stick to singly linked lists (the simplest one). If for example you want a doubly linked list instead, very few simple modifications will give you what you're looking for. Many data structures (e.g. Stacks, Queues, Binary Trees) are often implemented using the concept of linked lists. Some different types of linked lists are shown below: A few basic types of Linked Lists Singly Linked List Root node links one way through all the nodes. Last node links to null. Circular Linked List Circular linked lists have a reference to one node which is the tail node and all the nodes are linked together in one direction forming a circle. The benefit of using circular lists is that appending to the end can be done very guickly. Doubly Linked List Every node stores a reference to its previous node as well as its next. This is good if you need to move back by a few nodes and don't want to run from the beginning of the list. 21 There's a diagram to help you realize the main disadvantage of arrays but not Linked Lists Create an empty integer array. Fill it partially with some data.The array component without a number indicates allocated but unused space. This is space you could have used for something better. Add another element. We now have a full array to which we can not add any more elements. We can delete or replace, but we can not add. lf the array is full, you can not add more elements, because arrays have a fixed size. Linked Lists do not. Another drawback of arrays is that if you delete an element from the middle and want no holes in your array (e.g. (1, 2, 4, null) instead of (1, 2, null, 4)), you will need to shift everything after the deleted element down in O(n) time. If you're trying to add an element somewhere other than the very end of an array, you will need to shift some elements towards the end by one (also O(n) time) to make room for the new element, and if you're writing an application which needs to perform well and needs to do these operations often, you should consider using Linked Lists instead. This should help you understand Linked Lists: This is an empty linked list look like. The * is an empty node (each element in a linked list called a node) which has its next-node reference set to the first node in the list. Since we don’t have a first node, its next –node is a null pointer This is a Linked List with three nodess. Each node points to the next node in the chain. As mentioned above, * is an empty node with a reference to the first node. [3] is the last node in the chain with next ==null Here we have delete node [2], so node [1] (previosly pointing to [2]) now points to [3]. If we didn’t change the refernce, node [3] and any nodes behind it would have no references from your program and get lost. If this happens and you’re using C or C++, you have a memory leak. If you’re using Java, the nodes would get automatically garbage collected. Either way, make sure you update the refernces! For whatever reasons, we’ve decided to add node [2] back nodes [1] and [3]. The reference from [1] is set to [2], and the refence from [2] is set to the old reference of [1], which is [3] 22 Pointers Java does not have an explicit pointer type. Instead of pointers, all references to objects— including variable assignments, arguments passed into methods and array elements—are accomplished by using implicit references. References and pointers are essentially the same thing except that you can’t do pointer arithmetic on references (nor do you need to). Reference semantics also enable structures such as linked lists to be created easily in Java without explicit pointers; merely create a linked list node with variables that point to the next and the previous node. Then, to insert items in the list, assign those variables to other node objects. public class link { public int value; public link next; // value of element // reference to next // constructor public link(int n, link ln) { value = n; next = ln; } public static void main(String args []) { // initialize list head link head = null; // add some entries to list for (int i = 1; i <= 10; i++) head = new link(i, head); // dump out entries link p = head; while (p != null) { System.out.println(p.value); p = p.next; } } } Java does not have pointers, but instead uses implicit references. A line like: link next; Does not actually declare an object instance of class link, but rather a reference to an object instance. The line: head = new link(i, head); 23 Creates a new element for the list, sets its value, and then sets the object reference in "next" to point at the old list head (this approach means that the last element inserted will be at the head of the list). Saying: p = p.next; Simply picks up the "next" reference from the current node and makes it the current element being worked on. class Link { public int iData; // data item (key) public double dData; // data item public Link next; // next link in list // ------------------------------------------------------------public Link(int id, double dd) // constructor { iData = id; // initialize data dData = dd; // (‘next’ is automatically set to null) } // ------------------------------------------------------------public boolean isEmpty() // true if list is empty { return (first==null); } // ------------------------------------------------------------- The insertFirst() Method The insertFirst() method of LinkList inserts a new link at the beginning of the list. To insert the new link, we need only set the next field in the newly created link to point to the old first link and then change first so it points to the newly created link. This situation is shown in Figure below. The insertion it can be done in two steps: 1. Update the next link of a new node, to point to the current head node. 2. Update head link to point to the new node. 24 Inserting a new link. // insert at start of list public void insertFirst(int id, double dd) { // make new link Link newLink = new Link(id, dd); newLink.next = first; // newLink --> old first first = newLink; // first --> newLink } 25