Name: _____________________ Class: CET 375
SSN/ID:   _____________________ Section & Group: ____________
The Queues Tutorial


According to Webster's Dictionary, a queue is a "waiting line," such as a line of persons waiting to check out at a supermarket, a line of vehicles at a toll booth, a queue of planes waiting to land at an airport, or a queue of jobs in a computer system waiting for some output device such as a printer. In each of these examples, the items are serviced in the order in which they arrive; that is, the first item in the queue is the first to be served. Thus, whereas a stack is a Last-In-First-Out (LIFO) structure, a queue is a First-In-First-Out (FIFO) or First-Come-First-Served (FCFS) structure.

As a data structure, a queue is an ordered collection of data items with the property that items can be removed only at one end, called the front of the queue, and items can be added only at the other end, called the back of the queue. Basic operations are:

construct: Create an empty queue
empty: Check if a queue is empty
addQ: Add a value at the back of the queue
front: Retrieve the value at the front of the queue
removeQ: Remove the value at the front of the queue

In this tutorial, you will construct a Queue class and test it. For more information and some help with the code, you should look at Chapter 5 of the text C++: An Introduction to Data Structures. In the project, you will use your Queue class (and the Stack class from Chapter 4) in an application.

A. Beginning the Queue Class

  1. Begin by listing and briefly describing the basic queue operations in the opening documentation of queue.h. (See source_lab06_stack.h from Chapter 4 of the text for an example — but add your own personalizing information such as Written by: xxxxxxxx on mm/dd/yy ... and other documentation, as specified in the Programming Style Sheet.) Use the names empty, addQ, front, and removeQ for the basic operations.
  2. Add appropriate #ifndef ... pre-processor directives to queue.h (see Lab Exercise 3).
  3. You will be using a Queue of integers. Add an appropriate typedef declaration that makes QueueElement a synonym for int.
  4. Next, add the "class stub" (please see the Programming Style Sheet for more on this). This is basically just the skeleton of the class, with the comments.

B. Designing the Queue Class — Adding Data Members

  1. Because a queue resembles a stack in many ways, we might imitate our implementation of the Stack class ( source_lab06_stack.cpp). Add appropriate declarations of data members to queue.h.

    Note: you will not be designing the queue.cpp implementation file yet (that's at the very end of the tutorial, when you test your implementation with the qtester.cpp test driver program).

    For "standardization", let us agree that the front of the queue is "to the left" — that is, toward the lower array indices:

    0 1 2 3 4 ...
    myArray           ...
    front
    back

  2. We could imitate our first attempt at implementing a stack by keeping the front of the queue fixed at position 0 (so we wouldn't even need a myFront data member) and just let myBack change. However, this would mean shifting the array elements every time a value is removed from the queue. To demonstrate this, consider the following picture of a queue containing 77, 99, and 33:

    myBack = 3
    0 1 2 3 4 ...
    myArray 77 99 33     ...

    Draw diagrams like the preceding to picture the queue after each of the following operations:

  3. To avoid this shifting of array elements every time a value is removed from the queue, we might (naturally) imitate our improved array-based implementation of our Stack class:
    Algorithm
    To add a value to the queue: Store value in myArray[myBack] (provided myBack does not exceed the array capacity)
    Increment myBack by 1
    To remove a value from the queue: Increment myFront by 1

    For example, a queue containing 77, 99, and 33 — added in this order — would be pictured as:

    myBack = 3
    0 1 2 3 4 ...
    myArray 77 99 33     ...
    myFront = 0

    However, this implementation has problems. Demonstrate this by beginning with the diagram above and draw diagrams of the array after each of the following operations:

    Because there are still some open slots in the array, we should be able to add some new elements to the queue. However, we can't use the "add" algorithm. Why not? (Note: assume ARRAY_CAPACITY is 5.)






    If we want to add another value to the queue, the elements in the array must be shifted back to the beginning of the array (thus setting myFront back to 0). Show the queue after this is done and the value 90 is added to the queue.






  4. Using a cirrcular array: This shifting of array elements can be avoided if we think of the array as circular, with the first element following the last. This can be done by incrementing myFront and myBack using addition modulo the capacity of the array — that is, (x+1) % capacity. For example, suppose we start with the following three operations, where the array capacity is 5:

    Add 55 to myArray Add 66 to myArray Remove from myArray

    Continuing with the last diagram on the preceding page, draw similar diagrams to picture the following operations:

        Add 44 and 88       Remove two elements        Add 77
    
    
    
    
    
        
        Add 111 and 222     Remove three elements
    
    
    
    
        
    
        
    For this circular-array implementation, our add and remove algorithms become:

    Algorithms
    To add a value to the queue: Store value in myArray[myBack] (provided there is room)
    Replace myBack by (myBack + 1) % (ARRAY_CAPACITY)
    (e.g., for array size 5, myBack = 4 → (4+1)%5 = 0)
    To remove a value from the queue: Retrieve myArray[myFront]
    Replace myFront by (myFront + 1) % (ARRAY_CAPACITY)

  5. One more problem: Empty vs. Full

       Determining if a queue is empty:

    The queue in the last diagram above has a single element. Give values of myFront and myBack:

        myFront = __________________     myBack = __________________
        
    Now, draw a picture of the queue after this element is removed and give the new values of myFront and myBack:
    myFront = ________   myBack = ________
    
    	
        
     

       Determining if a queue is full:

    Now, draw a picture of the queue formed by adding four values: 11, 22, 33, and 44. Give values of myFront and myBack:
    myFront = ________   myBack = ________
    
    	
        
     

    Now, draw a picture of the queue after another value, 55, is added to the queue and give the new values of myFront and myBack:
    myFront = ________   myBack = ________
        
    From this, we see that the condition myFront and myBack are equal also indicates a full queue. Consequently, we are unable to distinguish between a full queue and an empty queue!
     

Solutions

There are several different methods that can be used to solve this problem.

One solution is to add a data member, mySize, that keeps track of the number of values stored in the queue. Then, mySize == 0 indicates an empty queue and mySize == ARRAY_CAPACITY indicates a full queue. However, mySize must be incremented on every add operation and decremented on every remove operation.

A slightly more efficient solution, and the one that we'll use here, is to keep a gap between the front and the back of the queue so that the situation above never happens. Then, our empty and full algorithms become:

Final Algorithms
To check if a queue is empty: Check if myFront and myBack are equal.
To check if a queue is full: Compute newBack = (myBack + 1) % ARRAY_CAPACITY.
Then, check if myFront and newBack are equal.
To add a value to the queue: Check if the queue is full (algorithm above)
Store value in myArray[myBack]
Replace myBack by (myBack + 1) % (ARRAY_CAPACITY)
To remove a value from the queue: Replace myFront by (myFront + 1) % (ARRAY_CAPACITY)

C. Function Members for the Queue Class

Add functions to the Queue class for the following basic queue operations (implementing the above Final Algorithms). Test each one as you add each new operation. When you're done, use the program source_lab06_qtester.cpp to test your class.

• Constructor Initializes an empty queue by setting myFront and myBack to 0. (Note: because we're using a circular array, it really doesn't matter where we start!)
<< Overload the output (insertion) operator << so that a statement of the form cout << q; will display q's contents from front to back.
empty() For these, use the algorithms described above and on the previous page.
addQ()
front()
removeQ()



Hand In:

  1. This lab handout with the questions answered and the diagrams filled in.
  2. Printouts showing a listing of your Queue class, both the header and the implementation file (use the enscript command from your Programming Style Sheet:
    enscript -E -G -2rj -M Letter -PECT2_PS <filename>).
    Note: Be sure your header file has complete opening documentation adn complete documentation of functions!
  3. Printouts showing a sample run with qtester.cpp (use the enscript command from your Programming Style Sheet to print it out:
    enscript -E -G -2rj -M Letter -PECT2_PS qtester.cpp).
Category Points Possible Points Received
The Tutorial (35) __________
Class Queue (95)
   Correctness of class (including following instructions) 60 __________
   Testing with qtester.cpp 15 __________
   Documentation 15 __________
   Style/Readability (white space, alignment, etc.) 5 __________

TOTAL

130

__________


Ricky J. Sethi <rickys at sethi.org>
Last modified: Mon Jun 9 16:26:21 PDT 2003