Data Structures and Algorithms
CS 503

Quiz 1, 23 February 2011


  1. Explain how a free list improves performance in a linked list implemented with two arrays.


    The free list threads through the next-index array to keep track of the unused array elements, which makes finding an unused array element fast (independent of the array size).

    Without a free list, finding a free array element requires searching the next-index array for an unused array element, which takes time proportional to the array size.

  2. Given the 2x2 design matrix

    the end-choice 2x2

    what is the smallest number of check-marks needed to describe a data structure that can act as either a stack or a queue? Justify your answer.


    Three check marks are needed to describe a data structure that can act as either as a queue or a stack (or both). To be a stack, elements should be added to and removed from the head; to be a queue, elements should be added to the tail and removed from the head:

    the end-choice 2x2

  3. A stack can be implemented using a linked list. How many queues would it take to implement a deque? Justify your answer. The deque operations should should be fast. Note the queue internals are unavailable for use; that is, the deque implementation is strictly a queue client.


    It's not possible to implement a deque having fast operations using queues because elements can be added and removed at either end of a deque, and a queue only supports one operation at each end.

    It is possible to implement a deque using several queues, but supporting put and get at both ends requires shifting values between queues, which takes time proportional to the number of elements in the deque and so is not fast.

  4. Given a circular buffer, describe how to implement a fast size() method without using an instance variable keeping track of the number of elements. Clearly state your assumptions.


    The easiest solution is to assume the tail index points to the next free array location to receive a new element, if such a free location exists (otherwise the array is full and the size is n, the array size). With that assumption, the size would be the head minus the tail pointer, except the head may wrap around and be behind (be less than) the tail pointer. The easiest way to deal with the head lagging the tail is to add n to the head to make sure it's never less than the tail. However, the resulting difference has to be reduced modulo n to (possibly) adjust the difference back into range.

    Alternatively, the head and tail indices could be kept as always increasing values, which never let the head lag the tail and allow a simple subtraction to find the size (it also makes the difference between full and empty buffers obvious). However, the head and tail indices would have to be reduced modulo n when used to access an array element, which is an expensive operation to perform on each array access.

  5. A colleague of yours discovered a fast way to insert a value before a referenced value (that is, insert up-stream rather than down-stream) in a linked list. What technique did your colleague use? Hint: the reference used in the insertion is invalidated by the insertion.


    Your colleague inserted the new value as a successor to the given value, and then swapped the two values to restore the proper order, which invalidated references to the old value.

    Adding a successor is always fast because it follows the next pointers; adding a predecessor is not fast because it goes against the next pointers, requiring a trace down from the head. A doubly linked list has a fast insert-before operation, but it doesn't invalidate any references when used, unlike your colleague's solution.


This page last modified on 2011 February 23.