CS 367 - Introduction to Data Structures - Section 3

Written Assignment Zero: O-Notation and Run-Time Analysis

Due Thursday, October 9, 1997 at 2:30pm

Contents


Instructions

Answer the questions below clearly and succinctly. State why your answer is so, but keep your explanation simple.

If you need help, please see Marc Dreyfuss (the TA). Marc will be holding additional office hours during class times next week (9/30 and 10/2).


0. O-Notation

Recall the formal definition of O-notation from class: Given functions f and g, function f is said to be O(g) if there exist positive constants k and N such that for all n >= N, f(n) <= kg(n).
a.
Let f(n) = 2n3 + 47 and g(n) = (n-1)4. Find constants k,N that demonstrate that f=O(g).
b.
Prove or disprove (Yikes! I know I said there won't be many proof-type questions in this class, but I can't go the whole semester without making you at least try a few. This is the only one on this assignment. I promise.): If f=O(g) and g=O(h) then f=O(h). Hint: To disprove this claim, show three specific functions, f, g, and h for which this is not true. To prove the claim, assume there are constants k0,N0 that demonstrate f=O(g) and constants k1,N1 that demonstrate g=O(g); provide constants k2,N2 expressed in terms of the other constants that demonstrate that f=O(h).
c.
Explain why the statement, ``The running time of the algorithm A is at least O(n2),'' is content-free. (This problem was taken from this book.)

1. Fibonacci Numbers

Fibonacci numbers are numbers in the sequence 0,1,1,2,3,5,8,13,... The next element in the Fibonacci sequence is computed by adding the two previous elements. Fibonacci numbers have interesting mathematical properties and also occur in nature (for example, the arrangements of the petals of a sunflower). For further information on Fibonacci numbers and their history, see: http://www.mcs.surrey.ac.uk/Personal/R.Knott/Fibonacci/fib.html.

The Fibonacci function, fib, is a function that takes a non-negative integer n and returns the nth number in the Fibonacci sequence. fib(n) can be specified recursively:

fib(n) =
		0		if n = 0
		1		if n = 1
	fib(n-1) + fib(n-2)	otherwise

There are multiple ways the Fibonacci function can be implemented in C++. Two such ways are shown below. The first is recursive (like the above definition) - the function calls itself. The second is iterative - it uses a loop rather than recursion. More information on recursion and recursive functions can be found on the class web site at: http://www.cs.wisc.edu/~cs367-3/Notes/recursion.txt, as well as in Main & Savitch (Chapter 9) and Wang (Section 3.2).

// FILE: fib-rec.C 
// compute the nth Fibonacci number, recursively 
unsigned fib(unsigned n) 
{ 
  unsigned f; 
  
  if (n==0)
    f = 0;
  else if (n==1)
    f = 1;
  else
    f = fib(n-1) + fib(n-2);
  return f;
}


// FILE: fib-iter.C 
// compute the nth Fibonacci number, iteratively
unsigned fibIter(unsigned n)
{
  unsigned f, f1, f2;

  if (n==0)
    f = 0;
  else if (n==1)
    f = 1;
  else {
    f2 = 0; 
    f1 = 1; 
    for(unsigned i=1; i< n; i++) { 
      f = f1 + f2;
      f2 = f1;
      f1 = f; 
    } 
  } 
  return f; 
}
\noindent (This code, as well as a program to test it, can be found on the web at: http://www.cs.wisc.edu/~cs367-3/Programs/Fib/ .)
a.
What is the run-time complexity of the iterative version of the Fibonacci function (fibIter) in terms of O-notation?
b.
What is the run-time complexity of the recursive version of the Fibonacci function (fibIter) in terms of O-notation? (Hint: consider the trace of an execution of fib - see the notes at: http://www.cs.wisc.edu/~cs367-3/Notes/recursion.txt for details. How many times does a fib expression occur in the trace?)
c.
Comment (briefly) on the relative advantages of the two different implementations of the Fibonacci functions.

2. The Towers of Hanoi

The Towers of Hanoi is a game in which you begin with three spindles (the ``towers'') - two empty and one stacked with a selected number of discs in graduated sizes. Your object is to transfer the entire stack of discs to one of the other two spindles. The catch? You must do it by moving only a single disc at a time from one spindle to another, and you may never place a larger disc on top of a smaller one.

For more information (and a chance to play), check out these web sites:

The standard strategy for winning the Towers of Hanoi game is to think recursively. For example, if you need to move a stack of four discs from the first spindle to the third, you can solve the smaller problem of moving the three smallest discs to the second spindle, then move the fourth disc to the third spindle, and then once again solve the three disc problem, this time moving them on top of the fourth disc, on the third spindle.

This is illustrated in the following C++ code:

void move(unsigned n, unsigned A, unsigned B, unsigned C) 
// n : number to move
// A : source spindle, B : destination spindle, C : spare spindle
{
  if (n==1)
    cout << "Move the top disc from " << A << " to " << C << "\n";
  else {
    move(n-1,A,B,C);
    move(1,A,C,B); 
    move(n-1,B,C,A);
  }
}
(This code, along with a main function for testing it, can be found on-line at: http://www.cs.wisc.edu/~cs367-3/Programs/Hanoi/hanoi.C.)
a.
In terms of O-notation and n (the number of discs Towers of Hanoi is played with), what is the run-time complexity of the above move function?
b.
What does this say about how long it would take a person to play an 100-disc version of the game?

3. Bags vs. Lists & Arrays vs. Linked Lists

Thus far, we have considered two different kinds of container classes - bags and lists. We have also considered two different representations for these container classes. The goal of this exercise is to compare the worst-case complexities of several member functions of bags implemented using arrays, lists implemented using arrays, and lists implemented using linked lists.

Consider the insert function and the equality operator. Code for the three versions of insert is shown below:

void Bag::insert(int entry) 
{ 
  assert(!isFull());

  data[used] = entry;
  used++;
}
(This code, along with related code, can be found on-line at: http://www.cs.wisc.edu/~cs367-3/Programs/Bags/.)
void List::insert(int entry) 
{
  assert(!isFull());

  for(size_t i=used; i>0; i--)
    data[i] = data[i-1];
  data[0] = entry;
  used++;
}
(This code, along with related code, can be found on-line at: http://www.cs.wisc.edu/~cs367-3/Programs/Lists/Arrays/.)
void List::insert(int entry)
{ 
  ListItem *temp;

  assert(!isFull()); 

  // special case for empty list 
  if (isEmpty())
    list = new ListItem;
  else {
    temp = list;
    list = new ListItem;
    list->next = temp;
    temp->prev = list;
  }
  list->val = entry;
  used++; 
}
(This code, along with related code, can be found on-line at: http://www.cs.wisc.edu/~cs367-3/Programs/Lists/Linked/.)
a.
In terms of O-notation, what is the worst-case run-time complexity of Bag::insert?
b.
In terms of O-notation, what is the worst-case run-time complexity of List::insert, where the class List is implemented with arrays?
c.
In terms of O-notation, what is the worst-case run-time complexity of List::insert, where the class List is implemented with linked lists?
In class we have discussed the run-time properties of the equality operator (as defined in class) for Bag. Suppose we define an equality operator for lists such that two lists are said to be equal if they have the same elements in the same order.
d.
Write pseudocode for an equality operator for the class List implemented with arrays.
e.
In terms of O-notation, what is the worst-case run-time complexity of the equality operator for the class List, implemented with arrays?
f.
Write pseudocode for an equality operator for the class List implemented with linked lists.
g.
In terms of O-notation, what is the worst-case run-time complexity of the equality operator for the class List, implemented with linked lists?
h.
Compare the efficiency of the equality operator for the two list implementations as well as for the class Bag (discussed in class and as part of Programming Assignment Zero).
Suppose a sort function is added to the List class. For the array representation, an implementation using the selection sort algorithm might look like this:
void List::sort()
// sorts the list using a selection sort
{
  int min; 
  size_t k,minIndex;

  for(k=0; k+1 < used; k++) {
    minIndex = k; 
    min = data[k]; 
    for(size_t j=k+1; j < used; j++) {
      if (data[j] < min) {
        min = data[j]; 
        minIndex = j; 
      } 
    } 
    data[minIndex] = data[k];
    data[k] = min;
  } 
} 
i.
What is the run-time complexity of this algorithm, in terms of O-notation?
j.
Write pseudocode for a selection sort member function for the List class implemented with linked lists rather than arrays.
k.
Which of these two sort implementation is more efficient?
l.
Briefly discuss the relative merits of the two implementations of the class List discussed in class in terms of run-time efficiency.

4. Dynamic Arrays

a.
Read about dynamic arrays in Main & Savitch (Chapter 4, 137--177). (If you have not been able to purchase a copy, use the reserve copies available at Wendt Library.)
With dynamic arrays, (like with linked lists) container classes such as bag and list need not have a fixed capacity. Instead of disallowing an insert when a bag (or list) is full, we can resize the bag (or list) by creating a larger dynamic array, copying the data, and then discarding the old array. The following C++ function is adapted from Main & Savitch (p. 174):
void Bag::resize(size_t new_capacity)
{
  int *new_array; 

  if (new_capacity <= capacity) // no need to create more space 
    return; 

  new_array = new int[new_capacity]; 
  for(size_t i=0; i<used; i++) 
    new_array[i] = data[i]; 
  delete[] data; 
  data = new_array; 
  capacity = new_capacity; 
} 
b.
What is the complexity (in terms of O-notation) of Bag::resize?
c.
Is such resizing needed for a linked-list representation of a container class? Why or why not?
d.
Which representation makes more sense if the size of the container class is likely to change frequently? Explain.

References

1.
Thomas H. Cormen, Charles E. Leiserson, and Ronald L. Rivest. Introduction to Algorithms. The MIT Press, 1990.

2.
Michael Main and Walter Savitch. Data Structures and Other Objects Using C++. Addison Wesley, 1997.

3.
Paul S. Wang. C++ with Object-Oriented Programming. PWS Publishing Company, 1994.

Grading

The assignment has been graded on an 100 point scale. A grading key is now available.

For more information on general grading policy, click here.


Submitting

Turn in a written (hard-copy) solution at the beginning of class on the day it is due.

Solution

Marc Dreyfuss has put together a solution based on your own answers. This can be contrasted with the instructor's solution.


Late Policy

I'd like to discuss the solutions to the assignment on the day it is due. This is only possible if assignments are not late. If you feel you truly have a justifiable excuse for turning in the assignment late, you must clear it with me at least one day in advance.