Presentation is loading. Please wait.

Presentation is loading. Please wait.

Queues Lecture 4. Lecture Objectives  Learn about queues  Examine various queue operations  Learn how to implement a queue as an array  Learn how.

Similar presentations


Presentation on theme: "Queues Lecture 4. Lecture Objectives  Learn about queues  Examine various queue operations  Learn how to implement a queue as an array  Learn how."— Presentation transcript:

1 Queues Lecture 4

2 Lecture Objectives  Learn about queues  Examine various queue operations  Learn how to implement a queue as an array  Learn how to implement a queue as a linked list  Examine the STL class queue  Discover queue applications  Learn about priority queue  Examine the STL class priority_queue In this lecture, you will:

3 Queues  The notion of a queue in computer science is the same as the notion of the queues to which you are accustomed in everyday life.  Examples: 1 – There are queues of customers in a bank. 2 – There are queues of customers in a grocery store. 3 – There are queues of cars waiting to pass through a tollbooth. 4 - Similarly, because a computer can send a print request faster than a printer can print, a queue of documents is often waiting to be printed at a printer.

4 A bank line at time (a) 0; (b) 12; (c) 20; (d) 38

5 Queues  Definition: A queue is a set of elements of the same type in which the elements are added at one end, called the back or rear, and deleted from the other end, called the front or first  First In First Out (FIFO) data structure

6 FIFO First In First Out (FIFO) data structure The general rule to process elements in a queue is that the customer at the front of the queue is served next and that when a new customer arrives, he or she stands at the end of the queue. That is, a queue is a First In First Out, or simply FIFO data structure.

7 ADT Queue Operations 1.Create an empty queue. 2.Destroy a queue. 3.Determine whether a queue is empty. 4.Add a new item to the queue. 5.Remove from the queue the item that was added earliest. 6.Retrieve from the queue the item that was added earliest.

8 Figure (a)A naive array-based implementation of a queue; (b)rightward drift can cause the queue to appear full An Array-Based Implementation For applications in which a fixed-sized queue does not present a problem, you can use an array to represent a queue. As figure (a) illustrates, a naive array-based implementation of a queue might include the following definitions: const int MAX_QUEUE = maximum-size-of-queue; typedef desired-type-of-queue-item QueueItemType; QueueItemType items[MAX_QUEUE]; int front; intback; Here front and back are the indices of the front and back items, respectively, in the queue. Initially, front is 0 and back is -1. To insert a new item into the queue, you increment back and place the item in items[back]. To delete an item, you simply increment front. The queue is empty whenever back is greater than front. The queue is full when back equals MAX_QUEUE -1. The problem with this strategy is rightward drift – that is, after a sequence of additions and removals, the items in the queue will drift toward the end of the array, and back could equal MAX_QUEUE – 1 even when the queue contains only a few items. Figure (b) illustrates this situation.

9 One possible solution to this problem is to shift array elements to the left, either after each deletion or whenever back equals MAX_QUEUE – 1. This solution guarantees that the queue can always contain up to MAX_QUEUE items. Shifting is not really satisfactory, however, as it would dominate the cost of the implementation. An Array-Based Implementation

10 Figure A circular implementation of a queue A much more elegant solution is possible by viewing the array as circular, as Figure illustrates. You advance the queue indexes front (to delete an item) and back (to insert an item) by moving them clockwise around the array An Array-Based Implementation

11 Figure illustrates the effect of a sequence of three queue operations on front, back, and the array. When either front or back advances past MAX_QUEUE – 1, it wraps around to 0. This wraparound eliminates the problem of rightward drift, which occurred in the previous implementation, because here the circular array has no end. The only difficulty with this scheme involves detecting the queue-empty and queue-full conditions.

12 Figure (a) front passes back when the queue becomes empty An Array-Based Implementation It seems reasonable to select as the queue-empty condition front is one slot ahead of back since this appears to indicate that front “passes” back when the queue becomes empty, as Figure (a) depicts. However, it is also possible that this condition signals a full queue: Because the queue is circular, back might in fact “catch up” with front as the queue becomes full; Figure (b) illustrates this situation. Figure (b) back catches up to front when the queue becomes full

13 An Array-Based Implementation Obviously, you need a way to distinguish between the two situations. One such way is to keep a count of the number of items in the queue. => => Before inserting into the queue, you check to see if the count is equal to MAX_QUEUE: if it is, the queue is full. => => Before deleting an item from the queue, you check to see if the count is zero; if it is, the queue is empty.

14 To initialize the queue, you set front to 0, back to MAX_QUEUE – 1, and count to 0. You obtain the wraparound effect of a circular queue by using modulo arithmetic (that is, the C++ % operator) when incrementing front and back. For example For example, you can insert newItem into the queue by using the statements back = (back+1) % MAX_QUEUE; items[back] = newItem; ++count; Notice that if back equaled MAX_QUEUE -1 before the insertion of newItem, the first statement, back = (back+1) % MAX_QUEUE, would have the effect of wrapping back around to location 0. Similarly, you can delete the item at the front of the queue by using the statements front = (front+1) % MAX_QUEUE; --count; An Array-Based Implementation

15 Sometimes you do not require a count of the number of items in the queue. One approach uses a flag isFull to distinguish between the full and empty conditions. The expense of maintaining an isFull flag is about the same as that of maintaining a counter. However, a faster implementation declares MAX_QUEUE + 1 locations for the array items, but uses only MAX_QUEUE of them for queue items. You sacrifice one array location and make front the index of the location before the front of the queue. An Array-Based Implementation

16 Figure: A more efficient circular implementation (a) a full queue; (b) an empty queue queue is full As Figure illustrates, the queue is full if front equals (back+1) % (MAX_QUEUE+1) queue is empty But the queue is empty if front equals back This implementation does not have the overhead of maintaining a counter or flag, and so is more efficient time-wise. An Array-Based Implementation

17 of the ADT Queue An Array-Based Implementation of the ADT Queue

18 An Array-Based Implementation of the ADT Queue class Queue { public: // constructors and destructor: Queue(); // default constructor // copy constructor and destructor are supplied by the compiler // Queue operations: bool isEmpty() const; void enqueue(QueueItemType newItem); void dequeue(); void dequeue(QueueItemType& queueFront); void getFront(QueueItemType& queueFront) const; void display() const; private: QueueItemType items[MAX_QUEUE]; int front; int back; int count; }; // end Queue class Declaration of class Queue

19 An Array-Based Implementation of the ADT Queue Queue ::Queue() : front(0), back(MAX_QUEUE-1), count(0) { } // end default constructor bool Queue ::isEmpty() const { return bool(count==0); } // end isEmpty default constructor Function to check whether queue is empty or not

20 An Array-Based Implementation of the ADT Queue void Queue ::enqueue(QueueItemType newItem) { if(count == MAX_QUEUE) cout << "queue full on enqueue"; else { // queue is not full; insert item back = (back+1) % MAX_QUEUE; items[back] = newItem; ++count; } // end if } // end enqueue Function to add an item to the queue

21 An Array-Based Implementation of the ADT Queue void Queue ::dequeue() { if (isEmpty()) cout << "empty queue, cannot dequeue" ; else { // queue is not empty; remove front front = (front+1) % MAX_QUEUE; --count; } // end if } // end dequeue Function to remove front item from the queue

22 An Array-Based Implementation of the ADT Queue void Queue ::dequeue(QueueItemType& queueFront) { if (isEmpty()) cout << "empty queue, cannot dequeue\n" ; else { // queue is not empty; retrieve and remove front queueFront = items[front]; front = (front+1) % MAX_QUEUE; --count; cout << "Front element of the queue is “; cout << queueFront << endl; } // end if } // end dequeue Function to retrieve and remove front item from the queue

23 An Array-Based Implementation of the ADT Queue void Queue ::getFront(QueueItemType& queueFront) const { if (isEmpty()) cout << "empty queue, cannot getFront" ; else { // queue is not empty; retrieve front queueFront = items[front]; cout << "Front element of the queue is “; cout << queueFront << endl; } } // end getFront Function to retrieve front item of the queue

24 An Array-Based Implementation of the ADT Queue void Queue ::display() const { cout << "Items in the queue : ["; i = front - 1; do { i = (i+1)% MAX_QUEUE; cout << setw(3) << items[i]; } while (i != back) cout << " ]\n"; } // end display Function to display all the items of the queue

25 An Array-Based Implementation of the ADT Queue #include const int MAX_QUEUE = 5; typedef int QueueItemType; using namespace std; int main() { int a; Queue aQueue; aQueue.enqueue(15); aQueue.enqueue(20); aQueue.enqueue(25); aQueue.enqueue(33); aQueue.display(); aQueue.getFront(a); aQueue.dequeue(); aQueue.display(); aQueue.dequeue(a); aQueue.display(); system ("pause"); return 0; } Output Items in the queue : [ 15 20 25 33 ] Front element of the queue is 15 Items in the queue : [ 25 33 ] Front element of the queue is 25 Items in the queue : [ 33 ] Press any key to continue... Sample Run MAX_QUEUE = 5, means size of the queue is 5.

26 Figure: A pointer-based implementation of a queue (a)a linear linked list with two external pointers (b)a circular linear linked list with one external pointer A pointer-based implementation of a queue could use a linear linked list with two external pointers, one to the front and one to the back, as Figure (a) illustrates. Figure (b) shows that you can actually get by with a single external pointer – to the back – if you make the linked list circular. A Pointer-Based Implementation

27 Figure: Inserting an item into a nonempty queue Insertion at the back and deletion from the front are straightforward. Figure illustrates the addition of an item to a nonempty queue. Inserting the new node, to which newPtr points, at the back of the queue requires three pointer changes: the next pointer in the new node, the next pointer in the current back node, and the external pointer backPtr. Figure depicts these changes and indicates the order in which they can occur. (The dashed lines indicate pointer values before the changes.) A Pointer-Based Implementation

28 Figure: Inserting an item into an empty queue (a)before insertion (b)after insertion The addition of an item to an empty queue is a special case, as Figure illustrates. (a) (b) frontPtr backPtr newPtr backPtr frontPtr 3 3 frontPtr = newPtr; backPtr = newPtr; A Pointer-Based Implementation

29 Figure: Deleting an item from a queue of more than one item Deletion from the front of the queue is simpler than insertion at the back. Figure illustrates the removal of the front item of a queue that contains more than one item. Notice that you need to change only the external pointer frontPtr. Deletion from a queue of one item is a special case that sets the external pointers backPtr and frontPtr to NULL. A Pointer-Based Implementation

30 of of the ADT Queue (linked queue) A Pointer-Based Implementation of of the ADT Queue (linked queue)

31 A Pointer-Based Implementation of the ADT Queue class Queue { public: // constructors and destructor: Queue(); // default constructor Queue(const Queue& Q); // copy constructor ~Queue(); // destructor // Queue operations: bool isEmpty() const; // Determines whether the queue is empty. // Precondition: None. // Postcondition: Returns true if the queue is empty, otherwise returns false. void enqueue(QueueItemType newItem); // Inserts an item at the back of a queue. // Precondition: newItem is the item to be inserted. // Postcondition: If the insertion is successful, newItem is at the back of the queue.

32 A Pointer-Based Implementation of the ADT Queue void dequeue(); // Dequeues the front of a queue. // Precondition: None. // Postcondition: If the queue is not empty, the item // that was added to the queue earliest is deleted. void dequeue(QueueItemType& queueFront); // Retrieves and deletes the front of a queue. // Precondition: None. // Postcondition: If the queue is not empty, queueFront // contains the item that was added to the queue earliest, and the item is deleted. void getFront(QueueItemType& queueFront) const; // Retrieves the item at the front of a queue. // Precondition: None. // Postcondition: If the queue is not empty, queueFront // contains the item that was added to the queue earliest. void display(); // Displays the elements which are in the queue.

33 A Pointer-Based Implementation of the ADT Queue private: // The queue is implemented as a linked list with one external pointer to the front // of the queue and a second external pointer to the back of the queue. struct QueueNode { QueueItemType item; QueueNode *next; }; // end struct QueueNode *frontPtr; QueueNode *backPtr; QueueNode *curPtr; }; // end Queue class

34 A Pointer-Based Implementation of the ADT Queue Queue ::Queue() : backPtr(NULL), frontPtr(NULL) { } // end default constructor Queue ::Queue(const Queue& Q) { // Implementation left as an exercise. } // end copy constructor Queue ::~Queue() { while (!isEmpty()) dequeue(); // Assertion: frontPtr and backPtr equal NULL } // end destructor default constructor copy constructor destructor

35 A Pointer-Based Implementation of the ADT Queue bool Queue::isEmpty() const { return bool(backPtr == NULL); } // end isEmpty Function to check whether queue is empty or not.

36 A Pointer-Based Implementation of the ADT Queue void Queue::enqueue(QueueItemType newItem) { if (isEmpty()) { frontPtr = new QueueNode; frontPtr ->item = newItem; frontPtr ->next = NULL; backPtr = frontPtr; } else { // create a new node QueueNode* newPtr = new QueueNode; newPtr ->item = newItem; newPtr ->next = NULL; backPtr ->next = newPtr; backPtr = newPtr; } } // end enqueue Function to add an item to the queue

37 A Pointer-Based Implementation of the ADT Queue void Queue ::dequeue() { if (isEmpty()) cout << "empty queue, cannot dequeue" ; else { // queue is not empty; remove front QueueNode *tempPtr = frontPtr; if (frontPtr == backPtr) // special case? { // yes, one node in queue frontPtr = NULL; backPtr = NULL; } else frontPtr = frontPtr->next; tempPtr ->next = NULL; // defensive strategy delete tempPtr; } // end if } // end dequeue Function to remove front item from the queue

38 A Pointer-Based Implementation of the ADT Queue void Queue ::dequeue(QueueItemType& queueFront) { if (isEmpty()) cout << "empty queue, cannot dequeue\n" ; else { // queue is not empty; retrieve and remove front queueFront = frontPtr ->item; dequeue(); // delete front cout << "Front element of the queue is “ << queueFront << endl; } // end if } // end dequeue Function to retrieve and remove front item from the queue

39 A Pointer-Based Implementation of the ADT Queue void Queue ::getFront(QueueItemType& queueFront) const { if (isEmpty()) cout << "empty queue, cannot getFront" ; else { // queue is not empty; retrieve front queueFront = frontPtr ->item; cout << "Front element of the queue is " << queueFront << endl; } } // end getFront Function to retrieve front item of the queue

40 A Pointer-Based Implementation of the ADT Queue void Queue ::display() { curPtr = frontPtr; cout << "Items in the queue : ["; while (curPtr != NULL) { cout item; curPtr = curPtr ->next; } cout << " ]\n"; } // end display Function to display all the items of the queue

41 A Pointer-Based Implementation of the ADT Queue #include typedef int QueueItemType; using namespace std; int main() { int x; Queue aQueue; aQueue.enqueue(3); aQueue.enqueue(5); aQueue.enqueue(7); aQueue.enqueue(9); aQueue.display(); aQueue.getFront(x); aQueue.dequeue(); aQueue.display(); aQueue.dequeue(x); aQueue.display(); system ("pause"); return 0; } Sample Run Output Items in the queue : [ 3 5 7 9 ] Front element of the queue is 3 Items in the queue : [ 7 9 ] Front element of the queue is 7 Items in the queue : [ 9 ] Press any key to continue... QueueItemType is int type, that is, the queue can contain only integer numbers.

42 The Standard Template Library Class queue

43 THE queue INTERFACE template class queue { public: queue(); // default constructor queue(const queue&); // copy constructor ~queue(); // destructor queue& operator=(const queue&); // assignment operator int size() const; // returns number of elements bool empty() const; // returns true iff is empty T& front(); // returns the element in front T& back(); // returns the element in back void push(const T&); // inserts given element at back void pop(); // removes element from front };

44 Using a queue of Strings #include using namespace std; int main() { queue q; q.push ("Manoj"); q.push ("Sharaf"); q.push ("Wong"); q.push ("Azyyati"); q.push ("Norhana"); cout << "Size of queue is : " << q.size() << endl; cout << "\nFront element of the queue is "; cout << q.front(); cout << "\nBack element of the queue is "; cout << q.back(); cout << "\n\nElements in the queue :\n\n"; while (!q.empty()) { cout << q.front() << endl; q.pop(); } cout << endl; system("pause"); return 0; } Output Size of queue is : 5 Front element of the queue is Manoj Back element of the queue is Norhana Elements in the queue : Manoj Sharaf Wong Azyyati Norhana Press any key to continue... Sample Run

45 Simple Applications Of The ADT Queue Simple Applications Of The ADT Queue

46 Reading a String of Characters

47 When you enter characters at a keyboard, the system must retain them in the order in which you typed them. It could use a queue for this purpose, as the following pseudocode indicates: // read a string of characters from a // single line of input into a queue aQueue.createQueue() While (not end of line) { Read a new character ch aQueue.enqueue(ch) } // end while

48 Once the characters are in a queue, the system can process them as necessary. For example: If you had typed an integer – without any mistakes, but possibly preceded or followed by blanks – the queue would contain digits and possibly blanks. 247 247 If the digits are 2, 4, and 7, the system could convert them into the decimal value 247 by computing 10 * (10 * 2 + 4) + 7

49 // convert digits in a queue aQueue into a // decimal integer n // get first digit, ignoring any leading blanks do { aQueue.dequeue(ch) } while (ch is blank) // Assertion: ch contains first digit // compute n from digits in queue n = 0 done = false do n = 10 * n + integer that ch represents { n = 10 * n + integer that ch represents if (!aQueue.isEmpty()) aQueue.dequeue(ch) else done = true } while (!done and ch is a digit) // Assertion: n is result The following pseudocode performs this conversion in general:

50 Recognizing Palindromes

51 As you know that: a palindrome is a string of characters that reads the same from left to right as it does from right to left. As you have learned that you can use a stack to reverse the order of occurrences. You should realize by now that you can use a queue to preserve the order of occurrences. Thus, you can use both a queue and a stack to determine whether a string is a palindrome.

52 As you traverse the character string from left to right, you can insert each character into both a queue and a stack. Figure illustrates the result of this action for the string abcbd, which is not a palindrome. You can see that the first character in the string is at the front of the queue and the last character in the string is at the top of the stack. Thus, characters removed from the queue will occur in the order in which they appear in the string; characters removed from the stack will occur in the opposite order. Figure: The results of inserting a string into both a queue and a stack

53 Knowing this, you can compare the characters at the front of the queue and the top of the stack. If the characters are the same, you can delete them. You repeat this process until either the ADTs become empty, in which case the original string is a palindrome, or the two characters are not the same, in which case the string is not a palindrome. Figure: The results of inserting a string into both a queue and a stack

54 isPal(in str:string) : boolean // Determines whether str is a palindrome. // create an empty queue and an empty stack aQueue.createQueue() aStack.createStack() // insert each character of the string into both // the queue and the stack length = length of str for (i = 1 through length) { nextChar = i th character of str aQueue.enqueue(nextChar) aStack.push(nextChar) } // end for The following is a pseudocode version of a nonrecursive recognition algorithm for the language of palindromes: // compare the queue characters // with the stack characters While (aQueue is not empty and charactersAreEqual) { aQueue.getFront(queueFront) aStack.getTop(stackTop) if (queueFront equals stackTop) { aQueue.dequeue() aStack.pop() } else charactersAreEqual = false } // end while return charactersAreEqual

55 PRIORITY QUEUES

56 The use of a queue structure ensures that the items are processed in the order they are received. For example For example, in a banking environment, the customers who arrive first are served first. However, there are certain situations when this First In First Out rule needs to be relaxed somewhat. In a hospital environment, patients are, usually, seen in the order they arrive. Therefore, you could use a queue to ensure that the patients are seen in the order they arrive. PRIORITY QUEUES

57 However, if a patient arrives with severe or life threatening symptoms, they are treated first. In other words, these patients take priority over the patients who can wait to be seen, such as those awaiting their routine annual check-up. For another example, in a shared environment, when print requests are sent to the printer, interactive programs take priority over batch processing programs PRIORITY QUEUES

58 There are many other situations where some priority is assigned to the customers. priority queues To implement such a data structure in a program, we use special types of queues, called priority queues. In a priority queue, customers or jobs with higher priority are pushed to the front of the queue. One way to implement a priority queue is to use an ordinary linked list, which keeps the items in order from the highest to lowest priority. However, an effective way to implement a priority queue is to use a treelike structure. PRIORITY QUEUES

59 Here is part of the interface for the Standard C++ priority_queue container class template: STL class priority_queue template class priority_queue { public: priority_queue(); priority_queue(const priority_queue&); ~priority_queue(); priority_queue& operator=(const priority_queue&); int size() const; // returns number of elements bool empty() const; // returns true iff this is empty const T& top() const; // returns the front element void push(const T&); // inserts given element at back void pop(); // removes element from front private: //.... }; These are the same functions as for the queue class template. The only significant difference is that the priority_queue class requires the template parameter T to be an ordinal type (i.e., a type for which the comparison operators are defined) so that the pop() and top() functions remove and return the highest priority element instead of the first element.

60 EXAMPLE:Testing a Priority Queue of Integers EXAMPLE: Testing a Priority Queue of Integers USING priority_queue OBJECTS #include #include // defines the priority_queue class template using namespace std; typedef priority_queue PriorityQueue; void print(PriorityQueue); // prints a copy of given queue int main() { PriorityQueue pq; print(pq); pq.push(44); print(pq); pq.push(66); print(pq); pq.push(22); print(pq); pq.push(55); print(pq); pq.push(33); print(pq); system ("pause"); } void print(PriorityQueue q) { cout << "size=" << q.size(); if (q.size()>0) { cout << ", top=" << q.top() cout << ": (" << q.top(); q.pop(); while (!q.empty()) { cout << "," << q.top(); q.pop(); } cout << ")"; } cout << "\n"; } size=0 size=1, top=44: (44) size=2, top=66: (66, 44) size=3, top=66: (66, 44, 22) size=4, top=66: (66, 55, 44, 22) size=5, top=66: (66, 55, 44, 33, 22) Output

61 The print() function suggests that the elements are stored in the priority queue in descending order But that is probably not true. The function uses the priority_queue ::pop() function to access the elements, and that member function is required to remove the elements in order of their priority. But there is no obligation on the priority_queue itself to store the elements in any particular order. Indeed, most implementations keep the elements only partially ordered. PRIORITY QUEUES


Download ppt "Queues Lecture 4. Lecture Objectives  Learn about queues  Examine various queue operations  Learn how to implement a queue as an array  Learn how."

Similar presentations


Ads by Google