










Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Main points of this exam paper are: Alternative Algorithm, Same Static, Possible Definition, Computation Optimally., Computation Optimally, Alternative Algorithm, Minimum Cost, Multiplying Together, Return Multcost, Static Long Multcost
Typology: Exams
1 / 18
This page cannot be seen from the preview
Don't miss anything!











CS61B, Spring 2006 Final Examination P. N. Hilfinger
READ THIS PAGE FIRST. Please do not discuss this exam with people who haven’t taken it. Your exam should contain 10 problems on 18 pages. Officially, it is worth 50 points. This is an open-book test. You have three hours to complete it. You may consult any books, notes, or other inanimate objects available to you. You may use any program text supplied in lectures, problem sets, or solutions. Please write your answers in the spaces provided in the test. Make sure to put your name, login, and lab section in the space provided below. Put your login and initials clearly on each page of this test and on any additional sheets of paper you use for your answers. Be warned: my tests are known to cause panic. Fortunately, this reputation is entirely unjus- tified. Just read all the questions carefully to begin with, and first try to answer those parts about which you feel most confident. Do not be alarmed if some of the answers are obvious. Should you feel an attack of anxiety coming on, feel free to jump up and run around the building once or twice.
Your name: Login:
Page 2. /
Page 3. /
Page 4. /
Page 5. /
Page 6. /
Page 9. /
Page 10. /
Page 11. /
Page 12. /
Page 13. /
Page 14. /
Page 15. /
Page 16. /
Page 17–18. /
Login of person to your Left: Right:
Discussion section number or time:
Discussion TA:
Lab section number or time:
Lab TA:
a. If all the constructors of class Foo are private, no Foo’s can ever be created.
b. Suppose that variable L is an ArrayList<int[]>, and f is a destructive operation. Then to insure that we don’t lose the contents of the original int[] arrays in L after calling f, it suffices to write something like
/* Original value of L */ ArrayList
c. The legal Java fragment
byte b = 127; b += 1;
causes b to overflow to 0.
d. If x and y are local variables of type int, then no possible definition of swap will cause swap(x,y) to exchange their values.
e. If Foo.x is a private static variable defined in class Foo, then there is no way that a method call f(Foo.x) outside the class can modify the value of x (that is, the value of Foo.x after the call will be == to its value before).
f. If Foo.x is a static variable then the assignment Foo.x = null, if legal, will always modify that same static variable, regardless of where (in what class) the assignment appears.
g. If Foo.x is a static field whose type is AClass, then the call x.f() must call a method whose body is either written in the class AClass or else is inherited from its parent class.
b. Here is an alternative algorithm for the same problem:
/** The minimum cost of multiplying together k matrices of dimensions
private static long multCost (int[] M, int L, int U, long[][] work) { if (L+1 >= U) return 0; if (work[L][U] == 0) { long min; min = Long.MAX_VALUE; for (int i = L+1; i < U; i += 1) min = Math.min (min, multCost (M, L, i, work) + multCost (M, i, U, work)
Again, in the worst case, as a function of M.length, how many times do we compute the subexpression M[L]M[i]M[U]?
“Appropriate” means that it will lead to an algorithm that behaves well (doesn’t slow down inordinately) as input becomes large.
a. Implementing the Lisp (or Scheme) reader. This module takes identifiers (strings) and maps them to “symbols” (some kind of object) so that the same string always stands for the same symbol object and differing strings stand for different symbol objects. It operates throughout the execution of a Lisp program, creating new symbols as new identifiers are encountered during the reading of Lisp expressions from input.
b. Computing how best to intercept a fleeing car, knowing a map of the city and the presumed route of the car. We are interested here in the data structure used in doing this computation, assuming the data are already present.
c. Storing information about a set of points along a line that can be modified (that is, new points may be added or deleted from the set), and one can find points given an approximate position.
Problem continues on next page
interface Component { /** The input port named INPUTNAME. / Port getPort (String inputName); /* Connect my output to TOPORT. */ void connect (Port toPort); }
interface Port { /** Send input VAL to THIS. */ void sendInput (Object val); }
At most one output may be connected to any given port, but any given output may be connected to any number of ports (and must send each of its values to all of them). The class Contraption represents a set of named components
public class Contraption { /** Add COMPONENT to THIS, labeling it with NAME, which must be
/** Connect the output of the component named SOURCENAME
}
For example, here is how we might build a contraption that approximates π by generating a stream of random points in the unit square, and counting the number that fall inside the circle of radius 0.5 whose center is in the center of the unit square. This circle has area π/4, so we expect that to be the fraction of random points that fall in the circle. Multiplying that fraction by 4 should therefore give us successively better approximations of π. In this example, we assume that components that have a single input port name it just "In".
Contraption C = new Contraption(); C.add ("C1", new RandGen ()); C.add ("C2", new Pairer ()); C.add ("C3", new IsInside ()); C.add ("C4", new True ()); C.add ("C5", new CountTrue ()); C.add ("C6", new CountTrue ()); C.add ("C7", new Divider ()); C.add ("C8", new MultBy (4.0)); C.add ("C9", new Printer ());
// Continued C.connect ("C1", "C2", "In"); C.connect ("C2", "C3", "In"); C.connect ("C2", "C4", "In"); C.connect ("C4", "C5", "In"); C.connect ("C3", "C6", "In"); C.connect ("C5","C7","Divisor"); C.connect ("C6","C7","Dividend"); C.connect ("C7", "C8", "In"); C.connect ("C8", "C9", "In");
RandGen
C Pairer
C IsInside
C CountTrue
C Divider
C
MultBy 4
C
Printer
C
True
C
CountTrue
C
Here we have a RandGen sending random numbers to a Pairer, which spits out one pair of numbers for each two it receives. The pairs go to an insideness tester, which spits out a true value (the object Boolean.TRUE) for each pair it receives that is inside the circle, and a false for each that isn’t. The true values are counted by one of two CountTrues, which take in a stream of trues and falses, and output the cumulative number of trues received each time. By routing the output of the pair generator through a component that generates a value of true for every input it receives, and then counting the number of these true values, we get a count of the total number of pairs. The two counts then get divided, multiplied by 4 and printed. The Printer component never sends anything to its output, but simply prints each input it receives. The result is that the contraption prints a stream of (presumably) better and better approximations of π. To make this all do something, we assume that somewhere there is a loop
while (...) C.getComp ("C1").getPort ("In").sendInput ("");
and that the RandGen component spits out a random number each time it receives an input (any input) on its ‘In’ port.
b. Fill in the Pairer class, which takes a stream of Objects and outputs a pair (type Object[] with two members) for every two inputs it receives. That is, after receiving one input, say 0.75 (as a Double), it outputs nothing, and then after receiving a second input, say 0.3, it outputs the Double[] value {0.75,0.3}.
public class Pairer implements Component { public Pairer () { // FILL IN
Continued on next page
c. Fill in the Divider class, which accepts a Double input named "Dividend" and another named "Divisor" (in either order) and outputs their quotient as a Double when it has received both inputs. (So, if it first receives 25 on its Dividend port, it does nothing; when it next receives 50 on its Divisor port, it outputs 0.5 as a Double.) Do not worry about the situation in which you receive (for example) two Dividends before receiving a Divisor.
public class Divider implements Component { public Divider () { // FILL IN
a. Given the following red-black tree (shaded nodes are red):
Draw the corresponding (2,4) tree.
b. If a (2,4) tree has depth h (that is, the (empty) leaves are at distance h from the root), what is the maximum number of comparisons done in the corresponding red-black tree to find whether a certain key is present in the tree?
static IntList riffle (IntList L1, IntList L2, IntList pattern) {
if ( ) return L2;
if ( ) return L1; IntList result, last, p; result = last = p = null;
while ( ) { if (p == null) p = pattern; if (result == null)
= L1; else
= L1;
for (int i = 0; i < p.head && ; i += 1) {
last = ; L1 = L1.tail; } IntList t = L1; L1 = L2; L2 = t; p = p.tail; } if (L1 == null)
; else
; return result; }
d. Your program isn’t quite working properly. You discover that it contains a statement
L.head = y;
that sometimes inserts a null pointer into L.head, and in your program that is not supposed to happen. This line is executed millions of times in a typical run of the program. You’d like to be able to find out what is going on when this happens. What’s the quickest way to do so, preferably with minimal modification to your program and without generating reams of debugging output?
e. You are using an array with N elements to hold a heap (maximal element on top) of in the usual fashion. The heap is initially empty, and you add N items to it one at a time in descending order (re-heapifying after each addition). How many comparisons between items on the heap are required to do all N insertions?
f. What does the following computation compute?
int whatisthis(int x) { x = (0x55555555 & x) + (0x55555555 & (x >>> 1)); x = (0x33333333 & x) + (0x33333333 & (x >>> 2)); x = (0x0f0f0f0f & x) + (0x0f0f0f0f & (x >>> 4)); x = (0x00ff00ff & x) + (0x00ff00ff & (x >>> 8)); x = (0x0000ffff & x) + (0x0000ffff & (x >>> 16)); return x; }
/** A set of objects of type ITEM. */ public class BST<Item extends Comparable
/** An empty set */ public BST () { root = null; size = 0; }
/** Add item X to THIS. Has no effect if X is already present. */ public void add (Item X) { size += 1; // ERROR? root = add (root, X); }
/** The number of items in THIS. */ public int size () { return size; // ERROR? }
/** Remove item X from THIS. Has no effect if X is not present. */ public void remove (Item X) { size -= 1; // ERROR? root = remove (root, X); }
/** True iff X is present in THIS. */ public boolean contains (Item X) { Implementation removed. Assume it works. }
private Node add (Node T, Item X) { Implementation removed. Assume it works. }
Continues on next page.