Solutions to Review Questions for Prelim II

CS211 - Fall 2000

Please let me know via email (chew@cs.cornell.edu) if you find mistakes in the solutions.  Past experience indicates that I may have made some.

Problem 1

a) Write a Java class method named append that takes two non-empty linked lists L1 and L2 as parameters, and updates the last cell of L1 so that it points to the first cell of L2.  The  method does not return anything.  The lists are built using the ListCell class given below.

public static void append (ListCell L1, ListCell L2) {
	ListCell finger = L1;
	while (finger.getNext() != null)
		finger = finger.getNext();
	finger.setNext(L2);
}

b) What is the asymptotic complexity of your algorithm, expressed as a function of n1 and n2, where n1 is the number of elements in L1 and n2 is the number of elements in L2, respectively?  Justify your answer (briefly).
This is an O(n1) algorithm because the number of iterations of the while-loop is equal to n1.  Each iteration performs a constant amount of work.

class ListCell {
	protected Object datum;
	protected ListCell next;
	
	public ListCell (Object x, ListCell n) {
		datum = x;
		next = n;
	}
	public Object getDatum () {
		return datum;
	}
	public ListCell getNext () {
		return next;
	}
	public void setDatum (Object x) {
		datum = x;
	}
	public void setNext (ListCell l) {
		next = l;
	}
	public String toString () {
		if (next == null) return datum.toString();
		else return datum.toString + " " + next.toString();
	}
}

Problem 2

In this small GUI-based application, we have 3 lights represented by three objects of class JLabel, corresponding to the green, yellow and red lights of a traffic light signal. We have three buttons for switching on the corresponding light. Only one light can be on at a time, i.e. the lights behave like “radio buttons”. A light is switched on and off by changing the background color of the corresponding JLabel using the setBackground() method. A light is off when its color is gray (Color.gray). The colors green, yellow and red are represented by the constants Color.green, Color.yellow and Color.red respectively. Initially the green light is on. Note that these lights do not behave like true traffic lights as they are controlled by individual buttons. 

The GUI is shown in Figure (a) and the containment hierarchy is shown in Figure (b). Note that the picture is taken from an old exam (before javax.swing was released); thus, you should substitute JLabel for Label, JButton, for Button, JPanel for Panel, and JFrame for Frame.  The skeleton code is provided below. Write the method createGUI() which creates the GUI according to the containment hierarchy in Figure (b), and the method addListeners() which creates and adds the relevant listeners to the relevant sources. The user should be able to terminate the program when the “close-box” of the top-level window is clicked.

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Pseudo Traffic Lights
 */
public class PseudoTrafficLights extends JFrame {
	private JPanel lightsPanel;
	private JLabel redLightLabel;
	private JLabel yellowLightLabel;
	private JLabel greenLightLabel;
	private JPanel buttonsPanel;
	private JButton redLightBtn;
	private JButton yellowLightBtn;
	private JButton greenLightBtn;

// Constructor
public PseudoTrafficLights() {
	super("Traffic Lights");
	
	// Create the GUI
	createGUI();
	
	// Add the listeners to the sources
	addListeners();
	
	// Pack and show the window
	pack();		// Makes window fit tight around its contents
	setVisible(true);
}

// The main method
public static void main (String[] args) {
	new PseudoTrafficLights();
}

// Helper method to terminate the program
private void terminate() {
	dispose();
	System.exit(0);
}

// a. (13 points) Write method createGUI to set up the GUI.
private void createGUI() {
	redLightLabel = new JLabel(" ");   // changed
	yellowLightLabel = new JLabel(" ");
	greenLightLabel = new JLabel(" ");
	redLightLabel.setBackground(Color.gray);
	yellowLightLabel.setBackground(Color.gray);
	greenLightLabel.setBackground(Color.green);
	redLightLabel.setOpaque(true);     // added
	yellowLightLabel.setOpaque(true);  // added 
	greenLightLabel.setOpaque(true);   // added
	lightsPanel = new JPanel(new GridLayout(3,1));
	lightsPanel.add(redLightLabel);
	lightsPanel.add(yellowLightLabel);
	lightsPanel.add(greenLightLabel);
	redLightBtn = new JButton("Red");
	yellowLightBtn = new JButton("Yellow");
	greenLightBtn = new JButton("Green");
	buttonsPanel = new JPanel(new GridLayout(1,3));
	buttonsPanel.add(redLightBtn);
	buttonsPanel.add(yellowLightBtn);
	buttonsPanel.add(greenLightBtn);
	getContentPane().setLayout(new FlowLayout());
	getContentPane().add(lightsPanel);
	getContentPane().add(buttonsPanel);
}
// b. (12 points) Write method addListeners to add listeners to the sources
private void addListeners() {

	greenLightBtn.addActionListener( new ActionListener() {
		public void actionPerformed(ActionEvent evt) {
			greenLightLabel.setBackground(Color.green);
			yellowLightLabel.setBackground(Color.gray);
			redLightLabel.setBackground(Color.gray);
		}
	});

	yellowLightBtn.addActionListener( new ActionListener() {
		public void actionPerformed(ActionEvent evt) {
			greenLightLabel.setBackground(Color.gray);
			yellowLightLabel.setBackground(Color.yellow);
			redLightLabel.setBackground(Color.gray);
		}
	});
	
	redLightBtn.addActionListener( new ActionListener() {
		public void actionPerformed(ActionEvent evt) {
			greenLightLabel.setBackground(Color.gray);
			yellowLightLabel.setBackground(Color.gray);
			redLightLabel.setBackground(Color.red);
		}
	});
	
	addWindowListener( new WindowAdapter() {
		public void windowClosing(WindowEvent evt) {
			terminate();
		}
	});

}
}

Problem 3

(a) Java Nagila, a CS211 student, implemented a bunch of algorithms and came up with the following running times as a function of input size.  Using big-O notation, write down the simples and most accurate expressions for the complexity of these algorithms.

(b) You have an algorithm that runs in time O(log n).  Java Nagila claims to have found a better algorithm that runs in time O(log(n-1)).  Is either algorithm better than the other in an asymptotic sense?  Explain your answer briefly using the formal definition of big-O notation.
No, in the asymptotic sense they are both the same.  To see this, note that log(n-1) < log(n) for all n>2; thus, by definition, log(n-1) = O(log n) for c=1 and N=2.  Note also that n < (n-1)2 for n>3.  Taking the log of each side, we get log n < 2 log(n-1) for n>3.  Thus, by definition, log n = O(log(n-1)) for c = 2 and N = 3.

(c) The following method returns the ith element of an integer list.

public static int element (List list, int i) {
	if (i == 1) return list.first();
	else return element(list.rest(), i-1);
}

What is the asymptotic time complexity of accessing the mth element of a list using this method?  Express the complexity as a function of m and n, the length of the list.
To get to the mth element, we make m calls to element() where each call takes constant time.  Thus the total time is O(m).  [This method could be improved by checking for i < 0.]

Problem 4

Fill in the table below with the expected time for each operation. Use big-O notation. The operations are insert (place a new item in the data structure), find (test if a given item is in the data structure), getMin (return the value of the minimum item in the data structure and delete it from the data structure), and successor (given an item, return the successor of that item).
Data Structure insert find getMin successor
sorted array  O(n) O(log n) O(1)* O(log n)
unsorted array O(1) O(n) O(n) O(n)
balanced tree O(log n) O(log n) O(log n) O(log n)
hashtable O(1)** O(1)** O(n) O(n)
sorted linked list O(n) O(n) O(1)* O(n)
unsorted linked list O(1) O(n) O(n) O(n)

*For both the sorted array and sorted list we can't tell if the array is in descending or in ascending order.  In such a situation, it's reasonable to assume that the order used is the one that provides the best time bounds.  Thus, for getMin() in the sorted array we assume the array is in descending order so that we don't have to move any of the data after we get the min value.  For getMin() in the sorted linked list, we assume the array is in ascending order so that we can can quickly locate the minimum.

**For the Hash Table, I am assuming that table doubling is being used.

Problem 5

  1. Where is the smallest element in a Binary Search Tree?
    In the node you reach by starting at the root and always moving left until you can no longer move left.
  2. How long does it take to insert a new element into a heap? To return the smallest thing in a min-heap? To delete the smallest thing in a min-heap? To find the largest thing in a min-heap?
    insert = O(log n); findMin = O(1); deleteMin = O(log n). Finding the largest thing in a min-heap is expensive: O(n).

Problem 6

The following picture represents a BST (Binary Search Tree).

             (22)
           /      \
       (14)        (25)
      /   \       /    \
   (11)   (16) (23)     (40)
                  \     /
                 (24) (32)
  1. In what order are the nodes visited during a preorder traversal?  Inorder traversal?  Postorder traversal?
    preorder: 22, 14, 11, 16, 25, 23, 24, 40, 32
    inorder: 11, 14, 16, 22, 23, 24, 25, 32, 40
    postorder: 11, 16, 14, 24, 23, 32, 40, 25, 22
  2. Show the tree that results when 15 is inserted into the tree.
                 (22)
               /      \
           (14)        (25)
          /   \       /    \
       (11)   (16) (23)     (40)
               /      \     /
            (15)     (24) (32)
  3. Show the tree that results when 25 is deleted from the original tree.
                 (22)
               /      \
           (14)        (32)     Used the successor.
          /   \       /    \
       (11)   (16) (23)     (40)
                      \
                     (24)

Problem 7

For each of the following problems, choose the best of the listed data structures and explain why your choice is best. Assume that the data set is going to be large unless otherwise indicated.  Where several operations are listed you should assume, unless stated otherwise, that the operations occur with about equal frequency.
  1. Operations are Insert, DeleteMax, and DeleteMin.
    balanced tree or sorted doubly-linked list
    The balanced tree is better since all operations take O(log n) time. The sorted doubly-linked list is O(1) for DeleteMax and DeleteMin, but Insert is O(n); thus, the average time per operation is O(n).
  2. You have a dictionary containing the keywords of the Pascal programming language.
    ordered array or balanced tree
    In this situation the ordered array is best. Both data structures take time O(log n) to find an item. An ordered array takes longer to insert or delete, but we don't expect to suddenly decide to change the set of keywords for Pascal, so there shouldn't be any insertion or deletion. The ordered array is simpler to program and takes less space.
  3. You have a dictionary that can contain anywhere from 100 to 10,000 words.
    unordered linked-list or hash table
    The hash table is better since the operations require O(n) time for the linked-list and O(1) time for the hash table. For 10,000 words this could certainly be significant.
  4. You have a large set of integers with operations insert, findMax, and deleteMax.
    unordered array or Hashtable
    Neither data structure is good for this problem. An unordered array is slightly better since it has less overhead and it's easier to program.

Problem 8

You have a hash table of size m=11 and a (not very good) hash function h:

h(x) = (sum of the values of the first and last letters of x) mod m

where the value of a letter is its position in the alphabet (e.g., value(a)=1, value(b)=2, etc.). Here are some precomputed hash values:
word ape bat bird carp dog hare ibex mud koala stork
h 6 0 6 7 0 2 0 6 1 8

Draw a picture of the resulting hashtable (using chaining) after inserting, in order, the following words: ibex, hare, ape, bat, koala, mud, dog, carp, stork. Which cells are looked at when trying to find bird?  What is the load factor (lambda) for the hash table?

chaining
0 ibex bat dog
1 koala
2 hare
3
4
5
6 ape mud
7 carp
8 stork
9
10

If you try to find "bird" then you'll search cells ape and mud.  The load factor is n/m = 9/11.

Problem 9

The following questions refer to an implementation of an ADT with operations Insert, Delete, and isMember. Note that these are the only operations, so for this problem it is not an advantage for a data structure to allow more operations.
  1. Under what conditions would you use a balanced tree instead of hashing with chaining?
    If I needed a guaranteed worst-case time of O(log n) for Dictionary operations.   The good results for hashing are only expected time.
  2. Under what conditions would you use an unordered array instead of a balanced tree?
    If I were short of space or if the set to be stored is small.
  3. Under what conditions would you use a binary search tree instead of a heap?
    Always.  A BST does each of these operations in O(log n) expected time.  A heap does these operation no better than using an unordered list.
  4. What implementation would you use to get the best expected time for a search?
    Hashing with table doubling.  Expected search time is O(1).
  5. What implementation would you use to get the best worst-case time for a search?
    Any balanced tree scheme (234-tree or red-black tree) or even an ordered array.   These all provide worst-case time O(log n) for a search.

Problem 10

BST (Dictionary) Max-Heap (Priority Queue)
      5
    /   \
   3     6
  / \
 1   4
  \
   2
     6
   /   \
  3     4
 / \
1   2

For each of the preceding trees, find a sequence of appropriate operations that will produce it starting from an empty tree.
There are lots of sequences that will do this.  Here's one possible sequence for the BST: Insert 5, 3, 6, 1, 4, 2.  Here's one for the Max-Heap: Insert 6, 3, 4, 1, 2.

 Problem 11

The following questions are to be answered by making use of the Java Collections Framework.

  1. Implement a double-ended queue. Operations are addFront, addBack, getFront, getBack, removeFront, removeBack, and isEmpty. The get- versions get the item without removing it. The remove- versions remove the item without retrieving it. This matches the text's definition of a double-ended queue. What is the runtime for each operation?

    import java.util.*;
        class DoubleQueue {
        	List q;
        	
        	public DoubleQueue () {q = new LinkedList();}
        	
        	public void addFront (Object x) {q.add(0,x);}
        	
        	public void addBack (Object x) {q.add(x);}
        	
        	public Object getFront () {return q.get(0);}
        	
        	public Object getBack () {return q.get(q.size() - 1);}
        	
        	public void removeFront () {q.remove(0);}
        	
        	public void removeBack () {q.remove(q.size() - 1);}
        	
        	public boolean isEmpty () {return q.size() == 0;}
        }

    We are using the LinkedList class which is a doubly-linked list.  Thus, all operations take time O(1).

  2. Write a main program that prints its arguments in sorted order. What is the runtime if the number of arguments is n?
    This is the solution given in the online tutorial:

        import java.util.*;
        
        public class Sort {
        public static void main(String args[]) {
            List l = Arrays.asList(args);
            Collections.sort(l);
            System.out.println(l);
        }
        }
    There are other possible solutions. For instance "Arrays.sort(args)" can sort the args array directly without making it into a List. Note though that asList doesn't do any real copying (instead it allows List operations to occur directly on the array) and System.out.println gives a sensible result when applied to a List, but not when applied to an array.
  3. Suppose you have two maps, one mapping idNumber to name and another mapping idNumber to salary. Write code to print a table giving name and salary sorted by name. Write code to print another table giving name and salary sorted by salary. You can assume that names are unique, but you should not assume that salaries are unique. How much time does it take for each of these tables?
    For the first table, you can iterate through the map keys to get all the name/salary pairs. These can be stored in a SortedMap. For the second table, you can't use a SortedMap because there can be duplicate salaries. You can make a new class Pair that holds a name/salary pair and that implements Comparable. You'll need to provide a compareTo method that compares on salary alone. Iterate through the original maps to get the Pairs, store these in an ArrayList and then use Collections.sort().  It take time O(n log n) to prepare each table.  It's not possible to do any better since each table requires the sorting of n items.

    Here's some code for this problem.  This includes a main() method for testing the code.
    import java.util.*;
    class Temp {
    public static void main (String[] args) {
    	//Test data
    	HashMap idToName = new HashMap(), idToSalary = new HashMap();
    	idToName.put(new Integer(101),"Larry");
    	idToName.put(new Integer(102), "Moe");
    	idToName.put(new Integer(103), "Curly");
    	idToSalary.put(new Integer(101), new Integer(500));
    	idToSalary.put(new Integer(102), new Integer(500));
    	idToSalary.put(new Integer(103), new Integer(501));
    	printTables( idToName, idToSalary);
    }
    	
    public static void printTables (Map idToName, Map idToSalary) {
    	// Produce table of (name, salary) sorted by name
    	SortedMap tableA = new TreeMap();
    	for (Iterator it = idToName.keySet().iterator(); it.hasNext();) {
    		Integer id = (Integer) it.next();
    		tableA.put(idToName.get(id),idToSalary.get(id));
    	}
    	System.out.println(tableA);
    	
    	// Produce a table of (name, salary) sorted by salary
    	// We can get a set of pairs by using the entrySet() method
    	// on tableA, but we still have to sort them.  This can be
    	// done by placing them in a list and then sorting them
    	// using a Comparator.
    	List tableB = new ArrayList(tableA.entrySet());
    	Collections.sort(tableB, new ValueComparator());
    	System.out.println(tableB);
    }
    static class ValueComparator implements Comparator {
    	public int compare (Object x, Object y) {
    		Map.Entry ex = (Map.Entry) x;
    		Map.Entry ey = (Map.Entry) y;
    		// We assume that the salaries are Comparable
    		return ((Comparable) ex.getValue()).compareTo(ey.getValue());
    	}
    }
    	
    }