Queue ADT Cinda Heeren / Geoffrey Tien 1
PA1 testing Your code must compile with our private test file Any non-compiling submissions will receive zero Note that only functions that are called will be compiled Add full coverage of function calls to the given main.cpp Write your own full coverage tests in a custom main.cpp Add a target in the makefile to any new executable Write a function stub for any function that you are unable to complete A mostly empty implementation that returns a default value of the appropriate type Cinda Heeren / Geoffrey Tien 2
Queues Assume that we want to store data for a print queue for a student printer Student ID Time File name The printer is to be assigned to file in the order in which they are received A fair algorithm Example: Print queue Cinda Heeren / Geoffrey Tien 3
Classes for print queues To maintain the print queue we would require at least two classes Request class Collection class to store requests The collection class should support the desired behaviour FIFO (First In First Out) The ADT is a queue Cinda Heeren / Geoffrey Tien 4
Queues In a queue items are inserted at the back (end/tail) and removed from the front (head) Queues are FIFO (First In First Out) data structures fair data structures Applications include: Server requests Instant messaging servers queue up incoming messages Database requests Print queues Operating systems often use queues to schedule CPU jobs The waiting list for this course! (it s presumably fair) Various algorithm implementations Cinda Heeren / Geoffrey Tien 5
Queue operations A queue should implement at least the first two of these operations: enqueue insert item at the back of the queue dequeue remove an item from the front peek return the item at the front of the queue without removing it isempty check if the queue does not contain any items Like stacks, it is assumed that these operations will be implemented efficiently That is, in constant time Cinda Heeren / Geoffrey Tien 6
Queue implementation Consider using an array as the underlying structure for a queue, we could Make the back of the queue the current size of the array, much like the stack implementation Initially make the front of the queue index 0 Inserting an item is easy Using an array What to do when items are removed? Either move all remaining items down slow Or increment the front index wastes space Cinda Heeren / Geoffrey Tien 7
Circular arrays Trick: use a circular array to insert and remove items from a queue in constant time The idea of a circular array is that the end of the array wraps around to the start of the array 7 0 6 1 0 1 2 3 4 5 6 7 5 2 4 3 Cinda Heeren / Geoffrey Tien 8
The modulo operator The mod operator (%) calculates remainders: 1%5 = 1, 2%5 = 2, 5%5 = 0, 8%5 = 3 The mod operator can be used to calculate the front and back positions in a circular array Thereby avoiding comparisons to the array size The back of the queue is: (front + num) % arrlength where num is the number of items in the queue After removing an item, the front of the queue is: (front + 1) % arrlength Member attributes: int front; int arrlength; int* arr; int num; Cinda Heeren / Geoffrey Tien 9
Array queue example Queue q; q.enqueue(6); 0 01 front num 6 0 1 2 3 4 5 Insert item at (front + num) % queue.length, then increment num Cinda Heeren / Geoffrey Tien 10
Array Queue Example Queue q; q.enqueue(6); q.enqueue(4); q.enqueue(7); q.enqueue(3); q.enqueue(8); q.dequeue(); q.dequeue(); 01 2 front 6 num Insert item at (front + num) % queue.length, then increment num 54 3 4 7 3 8 0 1 2 3 4 5 Remove item at front, then decrement num and make front = (front + 1) % queue.length Cinda Heeren / Geoffrey Tien 11
Array Queue Example Queue q; q.enqueue(6); q.enqueue(4); q.enqueue(7); q.enqueue(3); q.enqueue(8); q.dequeue(); q.dequeue(); q.enqueue(9); q.enqueue(5); 2 front 5 num Insert item at (front + num) % queue.length, then increment num 34 5 7 3 8 0 1 2 3 4 5 Remove item at front, then decrement num and make front = (front + 1) % queue.length 9 Need to check that the back of the queue does not overtake the front Cinda Heeren / Geoffrey Tien 12
Array queue resizing Suppose we have an array-based queue and we have performed some enqueue and dequeue operations Then we perform more enqueues to fill the array How should we resize the array to allow for more enqueue operations? 12 5 76 33 2 41 front???????????? Cinda Heeren / Geoffrey Tien 13
Queue Implementation Using a Linked List Removing items from the front of the queue is straightforward Items should be inserted at the back of the queue in constant time So we must avoid traversing through the list Use a second node pointer to keep track of the node at the back of the queue Requires a little extra administration Member attributes: Node* front; Node* back; int num; Cinda Heeren / Geoffrey Tien 14
List Queue Example Queue q; q.enqueue(6); q.enqueue(4); q.enqueue(7); q.enqueue(3); q.dequeue(); front back 6 4 7 3 Cinda Heeren / Geoffrey Tien 15
Exercise Given a Queue class implemented with a (dynamic) circular array of 8 int elements and an integer size member, the following block of code is executed: Queue qa; qa.enqueue(34); qa.enqueue(29); qa.enqueue(11); qa.dequeue(); qa.enqueue(47); qa.dequeue(); Queue qb = qa; qa.enqueue(6); qb.enqueue(52); qb.dequeue(); qb.dequeue(); qa.dequeue(); Shallow Copy, Deep Copy What will be output as the contents of each queue qa and qb if: qb is created as a shallow copy of qa? qb is created as a deep copy of qb? Cinda Heeren / Geoffrey Tien 16
Recursion Cinda Heeren / Geoffrey Tien 17
Rabbits! What happens when you put a pair of rabbits in a field? More rabbits! Let s model the rabbit population, with a few assumptions: Newly-born rabbits take one month to reach maturity and mate Each pair of rabbits produces another pair of rabbits one month after mating Rabbits never die...and recursion Cinda Heeren / Geoffrey Tien 18
More rabbits... How many rabbit pairs are there after 5 months? Month 1: start 1 pair Month 2: first pair are now mature and mate 1 pair Month 3: first pair give birth to a pair of babies original pair + baby pair = 2 pairs Month 4: first pair give birth to another pair of babies, pair born in month 3 are now mature 3 pairs Month 1 2 3 4 5 6 1 1 2 3 5 8 Month 5: the 3 pairs from month 4, and two new pairs 5 pairs Month 6: the 5 pairs from month 5, and three new pairs 8 pairs And so on... Cinda Heeren / Geoffrey Tien 19
Fibonacci series The n th number in the Fibonacci series, fib(n), is: 0 if n = 0, and 1 if n = 1 fib(n 1) + fib(n 2) for any n > 1 e.g. what is fib(23) Easy if we only knew fib(22) and fib(21) The answer is fib(22) + fib(21) What happens if we actually write a function to calculate Fibonacci numbers like this? Cinda Heeren / Geoffrey Tien 20
Calculating the Fibonacci series Let s write a function just like the formula fib(n) = 0 if n = 0, 1 if n = 1, otherwise fib(n) = fib(n 1) + fib(n 2) int fib(int n) { if (n <= 1) return max(0, n); else return fib(n-1) + fib(n-2); } The function calls itself Cinda Heeren / Geoffrey Tien 21
Recursive functions The Fibonacci function is recursive A recursive function calls itself Each call to a recursive method results in a separate call to the method, with its own input Recursive functions are just like other functions The invocation (e.g. parameters, etc.) is pushed onto the call stack And removed from the call stack when the end of a method or a return statement is reached Execution returns to the previous method call Cinda Heeren / Geoffrey Tien 22
Recursive function anatomy Recursive functions do not use loops to repeat instructions But use recursive calls, in if statements Recursive functions consist of two or more cases, there must be at least one Base case, and Recursive case Cinda Heeren / Geoffrey Tien 23
Recursion cases The base case is a smaller problem with a known solution This problem s solution must not be recursive Otherwise the function may never terminate There can be more than one base case And base cases may be implicit The recursive case is the same problem with smaller input The recursive case must include a recursive function call There can be more than one recursive case Cinda Heeren / Geoffrey Tien 24
Analysis of fib(5) int fib(int n) { if (n <= 1) return max(0, n); else return fib(n-1) + fib(n-2); } 3 5 fib(5) 2 fib(4) fib(3) 2 1 1 1 fib(3) fib(2) fib(2) fib(1) 1 1 1 0 1 0 fib(2) fib(1) fib(1) fib(0) fib(1) fib(0) 1 fib(1) 0 fib(0) Later in the course we will explain how this is an extremely inefficient way to compute the Fibonacci series Cinda Heeren / Geoffrey Tien 25
Example Base cases Target is found, or the end of the array is reached Recursive case Recursive linear search Target not found, search subarray starting from next element // Recursive linear search int reclinsearch(int arr[], int next, int sz, int x) { if (next >= sz) // end of array reached return -1; else if (x == arr[next]) // target found return next; else // not found, search from different starting index return reclinsearch(arr, next + 1, sz, x); } Cinda Heeren / Geoffrey Tien 26
Readings for this lesson Carrano & Henry Chapter 13.1 13.2 (Queues) Chapter 2 (Recursion) Next class: Carrano & Henry, Chapter 15.1 (Trees) Cinda Heeren / Geoffrey Tien 27