Download Final Exam for Computer Programming and Problem Solving | CS 111 and more Exams Computer Science in PDF only on Docsity!
Wellesley College ◊ CS111 Computer Programming and Problem Solving ◊ Fall 1999
FINAL EXAM REVIEW PROBLEMS
The CS111 final exam is a self-scheduled exam held during the normal final exam period. It is an
open book exam: you may refer to any books, notes, and assignments. You may not talk to other
people about the exam, nor may you use a computer during the exam.
Here is a list of topics covered by the course that may be tested on the final exam:
- problem solving patterns: divide/conquer/glue, recursion, iteration (tail recursion,
loops);
- abstraction: method abstraction, data abstraction, abstraction barriers/contracts/APIs.
- modularity: constructing programs out of mix and match parts (e.g. generators, mappers,
filters, accumulators) that use standard interfaces (lists, trees, arrays).
- language components: primitives, means of combination, means of abstraction.
- control structures: sequencing, method invocation, conditionals ( if / else ), loops
( while , for ), return.
- data structures: objects, lists, binary trees, arrays.
- models: execution diagrams, object diagrams, invocation trees.
- Java methods: declaration vs. invocation; parameter declaration and use; formal vs.
actual parameters; scope of parameter names; void vs. non-void return types; using
return to return result; invocation model (create a frame in Java execution model).
- Java class declarations: instance variables, class (static) variables; constructor methods,
instance methods, class (static) methods; inheritance; abstract classes and methods.
- Java statements: local variable declarations, method invocations, assignments, if / else
conditionals, while loops, for loops, return ; security keywords ( public , protected ,
private )
- Java expressions: literals (numbers, booleans, characters, strings), variable references
(instance variables [e.g., foo.x], array subscripts [e.g., foo[i]], this , super ), constructor
method invocations ( new ), non- void method invocations, (arithmetic, relational, logical
- microworlds: BuggleWorld, TurtleWorld, PictureWorld, ListWorld, TreeWorld,
SketchWorld; Java graphics and GUIs.
Below are some problems intended to help you review material for the final exam. The problems
do not cover all of the topics listed above, so you should also review your notes and assignments.
The problems range in difficulty. Some problems are from previous final exams. Others (with
some rewriting) could be turned into reasonable final exam problems. Others are too long or
complex for a final exam problem, but review material that is covered on the exam.
The problems are not in any particular order, so you should not feel compelled to do them in
order. Rather, you should first work on those problems that cover material in which you think
you need the most practice. To help you decide which problems to work on, each problem lists
the concepts that the problem covers.
As of this writing, there are no written solutions to these problems. You are encouraged to talk to
your instructors and your classmates about solutions to these problems. Solutions to some
problems will be presented in review sessions.
Problem 1: Squares (Tests Recursion, Iteration, TurtleWorld, BuggleWorld, PictureWorld,
GraphicsWorld)
Below are four parts that implement a similar problem in four different microwolds that we have
studied. In all parts, you should write any auxiliary methods that simplify the definition of the requested
method.
Part a. Write the following instnace method for a SquareTurtle subclass of Turtle:
public void squares ( int n, int len)
Draws n adjacent squares, the first of which has side length len, and the rest of which have a side
length that is one half the side length of the previous square. After drawing the squares, the position
and heading of the turtle should be the same as it was before drawing the squares. For instance, if
sara is a SquareTurtle facing EAST, then sara.squares(5, 240) should draw the following
picture and return sara to the same position and heading.
Part b. Write an instance method for a SquareBuggle subclass as Buggle that has the same
interface as the squares() method from Part a in which each square of side length len is drawn as a
len by len filled square of bagels.
Part c. Write the following PictureWorld method:
public Picture squares ( int n, Color c1, Color c2)
Returns a picture with n adjacent squares sitting at the bottom of the frame. The leftmost
square should fill the lower left quadrant of the frame. Each subsequent square should be
one-half the size of the square to its left. The colors of the squares should alternate between
c1 and c2 from left to right. Assume that public Picture patch (Color c) returns a
rectangular picture with color c that fills whole frame.
Problem 2: Greatest Common Divisor (Tests Iteration)
Wyla Lupe has been experimenting with ways to calculate the greatest common divisor (GCD)
of two integers. The GCD of two integers A and B is the largest integer that evenly divides into
both A and B. For example, the GCD of 30 and 18 is 6, the GCD of 28 and 16 is 4, and the GCD
of 17 and 11 is 1.
A clever algorithm for computing GCDs was developed by Euclid in 300 B.C. (In fact, it is
considered by many to be the oldest non-trivial algorithm!) Wyla has expressed Euclid's
algorithm in Java as the following tail recursive GCDTail method. (You do not have to
understand why the algorithm works!)
public static int GCDTail( int A, int B) { if (B == 0) { return A; } else { return GCDTail(B, A % B); } }
Recall that A % B (pronounced "A mod B") calculates the remainder of A divided by B. For
example, 10%3 is 1 , 10%4 is 2 , 10%5 is 0 , and 10%6 is 4.
Part a In the following table, show the sequence of values that the parameters A and B take on in
the iterative calculation of GCDTail(95,60). Important: You have been provided with more
rows than you need, so some rows should remain empty when you are done.
A B
Part b. In the following GCDWhile code skeleton, implement an alternative version of Euclid's
GCD algorithm that uses a while loop to express the same iteration that is expressed by Wyla's
GCDTail method. You may wish to introduce one or more local variables.
public static int GCDWhile ( int A, int B) { // Flesh out this skeleton }
Part c. Would it be easy to re-express Wyla's GCDTail program as a for loop? Briefly explain your
answer.
Problem 3: Array Reversal (Tests Arrays, Iteration)
Part a. Implement the following copyReverse() method on integer arrays:
public static int [] copyReverse ( int [] a);
Returns a new array that has the same length as a and all the elements of a in reverse order.
For example, suppose that p is the following array:
p (^17) -3 42 11 14
Then executing the statement
int [] q = copyReverse (p)
gives rise to the following diagram:
p (^17) -3 42 11 14
q 14 11 42 -3 17
Part b. Implement the following reverse() method on integer arrays:
public static void reverse ( int [] a);
Modifies a so that its elements are in the reverse of their original order. You should not create
any intermediate arrays.
For example, suppose that p is the following array:
r (^17) -3 42 11 14
Then executing the statement
reverse (r)
changes the diagram to be:
r
Problem 6: Converting Betweeen Different Forms of Iteration (Tests Lists, Arrays, Iteration)
We saw in class that iterations could be expressed as tail recursions, while loops, and for loops. Each
of the following parts contains a method that uses one of these forms of iteration. For each part, write
two equivalent methods that use the other two forms of iteration.
Part a.
public static int weightedSum (IntList L) { return weightedSumTail (L, 1, 0); }
public static int weightedSumTail (IntList L, int index, int total) { if (isEmpty(L)) { return total; } else { return weightedSumTail(tail(L), index + 1, (index*head(L)) + total); } }
Part b.
public static int isMember ( int n, int [] a) { int i = a.length - 1; while ((i >= 0) && (a[i] != n)) { i = i - 1; } return (i >= 0); // Will only be true if n is in a. }
Part c.
public static void partialSum ( int [] a) { int sum = 0; for (int i = 0; i < a.length; i++) { sum = sum + a[i]; a[i] = sum; } }
Part d.
public static void squiggle (Graphics g, int x1, int y1, int x2, int y2) { if ((x1 > 0) || (y1 > 0) || (x2 > 0) || (y2 > 0)) { g.drawLine(x1, y1, x2, y2); squiggle(g, x2, y2, y1/4, x1*2); } }
Problem 7: Converting Betweeen Arrays and Lists (Tests Lists, Arrays, Iteration)
Implement the following two methods for converting between lists and arrays of integers:
public static int [] listToArray (IntList L);
Returns an array of integers whose length is the same as the length of L and whose elements,
from low to high index, are in the same order as the elements of L.
public static IntList arrayToList ( int [] a);
Returns a list of integers whose length is the same as the length of a and whose elements are in
the same order as the elements of a (from low to high index).
Problem 8: Iterative List Reversal (Tests Invocation Trees, Recursion, Iteration, Lists,)
In class we studied the following recursive method for reversing a list:
public static IntList reverse (IntList L) { if (isEmpty(L)) { return empty(); } else { return postpend(reverse(tail(L)), head(L)); } }
public static IntList postpend (IntList L, int n) { if (isEmpty(L)) { return prepend(n, empty()); } else { return prepend(head(L), postpend(tail(L), n)); } }
This is not an efficient way to reverse a list. Each call to postpend() creates a new list whose length is
one more than the length of its first argument. Furthermore, postpend() is called once for each element in
the list being reversed. As a consequence, lots of intermediate list nodes are created that do not appear in
the final result.
Part a. Assume that A is the list whose printed representation is [1,2,3,4]. Assuming that reverse()
is implemented as shown above, draw an invocation treeand object diagram (i.e. box-and-pointer list
representations) for the invocation reverse(A). Your tree should have one node for each call to
reverse() and one node for each call to postpend().
Follow the conventions used in Problem 1 of Exam 2 for drawing invocation trees and object diagrams.
That is, your nodes should have the form
reverse( ListArgument ) : ListResult
postpend( ListArgument IntegerArgument ): ListResult
where IntegerArgument is an integer, and ListArgument and ListResult are references to list nodes
that appear in your object diagram.
Part b. An alternative technique for reversing a list is to follow the strategy one would use in reversing
a pile of cards: form a new pile by iteratively removing the top card of the original pile and putting it on
the new pile. When there are no more cards in the original pile, the new pile contains the cards in reverse
order from the original pile.
Based on this idea, here is a table corresponding to an iterative reversal of the list [1,2,3,4]:
list result [1, 2, 3, 4] [] [2, 3, 4] [1] [3, 4] [2, 1] [4] [3, 2, 1] [] [4, 3, 2, 1]
Implement this iterative list reversal strategy in a reverse() method in three ways: (1) using an
auxiliary tail recursive reverseTail() method to implement the iteration; (2) using a while loop to
implement the iteration; and (3) using a for loop to implement the iteration.
Part b. It is possible to express any iteration as a while loop. Flesh out the following code skeleton of a
partitionWhile() method that behaves just like the above partition() method except that it uses a
while loop rather than tail recursion to express the iteration of partitionTail(). Your
partitionWhile() method should not call any auxiliary methods other than twoLists() and the
IntList operations.
public static IntList [] partitionWhile ( int pivot, IntList L) {
Part c. In the above partition() method, elements in the two returned lists are in a relative order
opposite to their relative order in the original lists. For instance, partitioning the list [1, 4, 8, 3, 6,
7, 5, 2] about the pivot 6 yields the lists [2, 5, 3, 4, 1] and [7, 6, 8].
Suppose that we want the resulting lists to have the same relative order as in the original list. If we are
provided with a list reversal method reverse(), we can easily accomplish this by reversing the two lists
before putting them into the result array. That is, we can change the line
return twoLists(lesses, greaters);
within partitionTail() to be
return twoLists(reverse(lesses), reverse(greaters));
An alternative to using reverse() to get this behavior is to write partition() as a non-tail recursive
method. Flesh out the following skeleton of partitionNotTail() which partitions the elements of a list
about the pivot but maintains the relative order of the elements in the resulting lists:
public static IntList [] partitionNonTail ( int pivot, IntList L) { if (IL.isEmpty(L)) { return twoLists(IL.empty(), IL.empty()); } else { IntList [] subresult = partitionNonTail(pivot, IL.tail(L)); // flesh out the missing code here ... } }
Part d. An important use of the partition() method is a sorting algorithm known as quicksort. Here
is the idea behind quicksort:
To sort the elements of a list, partition the elements of the tail of the list around its
head into result lists that we'll call lesses and greaters. Then result of sorting the
whole list can be obtained by appending the result of sorting lesses to the result of
prepending the head of the list to the result of sorting greaters.
For example, if the initial list is [5, 2, 8, 3, 6, 7, 1, 4], then partitioning the tail of the list around
the head (5) yields:
lesses = [4, 1, 3, 2] greaters = [7, 6, 8]
By wishful thinking, sorting lesses will yield [1, 2, 3, 4] and sorting greaters will yield [6, 7, 8].
The result of sorting the original list is the result of appending [1, 2, 3, 4] to the result of prepending
5 to [6, 7, 8].
Flesh out a method public static IntList quicksort (IntList L) that uses this idea to sort the
elements of a list. You may use ILO.append() to append two lists.
Problem 10: Sales Statistics (Tests Data Abstraction, Arrays, Lists, Objects, Object Diagrams)
The management of the Decelerate Clothing Store (specializing in "clothes that slow you down") wants
to track certain statistics about customer purchases. In particular, they want to track the amount of each
purchase and whether it was made with cash or credit card. Later, they want to be able to calculate
statistics based on this information, such as the largest cash purchase amount, the average amount of a
credit card purchase, and the percentage of credit card purchases.
The management has hired Abby Stracksen of Simplistic Statistics to implement a Java program for
tracking the purchase information and calculating the desired statistics. Abby begins by designing a
contract for a History class that maintains a history of customer purchase:
Contract for the History Class
Constructor method for the History class
public History ( int maxEntries);
Returns a new History object that can store up to maxEntries purchase entries.
Instance methods for the History class
public void add ( int amount, boolean cash);
Adds a new purchase entry to this history: amount is the amount of the purchase;
cash value true indicates a cash purchase, cash value false indicates a credit card
purchase. An attempt to add an entry is ignored if maxEntries entries have been
stored.
public int size ();
Returns the number of entries in this history.
public int min ( boolean cash);
Returns the minimum amount of a purchase made with the given cash value.
(I.e. if cash is true, return the minimum purchase made with cash, otherwise return
the
minimum purchase made with credit card).
public int max ( boolean cash);
Returns the maximum amount of a purchase made with the given cash value.
public int average ( boolean cash);
Returns the average amount of a purchase made with the given cash value.
public int number ( boolean cash);
Returns the number of purchases made with the given cash value.
public int percentageByNumber ( boolean cash);
Returns the percentage (by number of purchases) of purchases made with the given
cash value.
public int percentageByAmount (boolean cash);
Returns the percentage (by total amount) of purchases made with the given cash
value.
Emil also started to implement the History class, but was called away on a business trip. Here's
how far he got:
public class History {
// Instance Variables: private Purchase [ ] purchases; private int size;
// Constructor Method: public History ( int maxEntries) { purchases = new Purchase[maxEntries]; size = 0; }
// Instance Methods: public void add ( int amount, boolean cash) { if (size < purchases.length) { purchases[size] = new Purchase(amount, cash); size = size + 1; } }
// I still need to finish the other methods! - Emil - }
Part a. Based on Emil's implementation, draw an object diagram that shows the result of
executing the following statements. Your diagram should include the local variable h and all
objects that are accessible from h via some sequence of pointers.
History h = new History(5); h.add(82, false); h.add(53, true); h.add(178, false);
Part b. Finish Emil's implementation by fleshing out the missing instance methods from his
History class.
Part c. Your colleague Bud Lojack believes that Emil could have written the constructor method
for Purchase as:
public Purchase ( int amount, boolean cash) { amount = amount; cash = cash; }
Is Bud right? Explain.
Part d. On his desk, Emil left the following notes about alternative implementations of the
Purchase class:
Many ways to implement Purchase instance. E.g.
1) As an array of two integers. Slot 0 = amount; Slot 1 = cash (use 0 for false, 1 for true).
2) As an integer list with two elements. First element = amount; second = cash.
3) As a single positive/negative integer. Amount is the absolute value. Positive indicates
cash; negative indicates credit.
4) As a single positive integer n. Amount = n / 2; cash = n % 2, where 0 is false, 1 is true.
Based on Emil's notes, provide four alternative implementations of the Purchase class that all
satisfy the Purchase contract.
Part e. Bud Lojack thinks that Emil should have made the amount and cash instance variables of
the Purchase class public rather than private. Explain to Bud why this is a bad idea.
Part f. Emil returns from his trip, and says that he had an epiphany about an alternative
representation of History instances that does not involve Purchase objects. Instead, Emil thinks
a history instance can be implemented as an object with three instance variables:
1. The maxEntries integer.
2. An integer list cashes holding the amounts of the cash purchases.
3. An integer list credits holding the amounts of the credit purchases.
Write an alternative implementation of the History class based on this representation.
Problem 11: Leftist Turtles (Tests Instance Variables, Class Declarations, Inheritance)
Supposewe want a LeftistTurtle subclass of Turtle that remembers the number of times that it has been
invoked with the lt() method. A LeftistTurtle has the same contract as Turtle except that it also
understands the following additional instance method:
public int numberOfLefts ();
Returns the number of times that the lt() method has been invoked on this turtle.
Part a. Write a complete class declaration for LeftistTurtle implementing the LeftistTurtle contract.
Part b. Consider the following test of the LeftistTurtle class:
LefistTurtle leo = new LeftistTurtle(); leo.lt(45); leo.rt(30); System.out.println(leo.numberOfLefts());
You might expect that executing the above statements should print 1, but, depending on the implementation of
the rt() method in Turtle, it might in fact print 2. Explain how this could happen. (Hint: study the
implementation of the TextTurtle discussed in class.)
Part c. Suppose that Turtle is implemented in such a way that the test code in Part b returns 2. Modify your
implementation of LeftistTurtle so that numberOfLefts() returns only the number of lt() method calls and
does not count the number of rt() method calls. You should only change LeftistTurtle; you should not
change Turtle!
public void transferFromSavingsToChecking( int transferAmount) { this .savings = this .savings - transferAmount; this .checking = this .checking + transferAmount; }
public void withdrawFromChecking( int withdrawalAmount) { this .checking = this .checking - withdrawalAmount; this .total = this .total - withdrawalAmount; }
}
Part a. An alternative representation for bank accounts is to keep track of only two integer instance
variables representing, respectively, the savings account balance and the total amount of money in the
bank (equal to the sum of the savings and checking account balances). This approach results in the
following object diagram representation:
savings
Account
0
total 0
Write an implementation of the Account class that uses this alternative representation. It is important to
note that it is only the internal representation of the bank account that has changed. The external
interface to the bank account itself remains the same. Additionally, every instance method should
behave the same as previously.
Part b. Yet another representation for bank accounts is to use only one instance variable: an integer
linked list that has three elements. The elements store, respectively, the savings account balance, the
checking account balance and the total amount of money in the bank (equal to the sum of the savings
and checking account balances). The resulting object diagram representation is:
Account
accountinfo
this element stores
the savings account
balance
this element stores
the checking
account balance
this element stores
the total of the
account balances
Write an implementation of the Account class that uses this representation. As in Part a of this
problem, it is important to note that it is only the internal representation of the bank account that
has changed. The external interface to the bank account remains the same. Additionally, every
instance method should behave the same as previously.
Your solution will use the IntList class discussed in class to represent an integer linked list.
When writing your code, you may assume, that IntList.head() may be abbreviated by head()
and similarly for the other IntList methods.
Problem 13: Number Pad (Tests GUI layout, GUI behavior)
Implement a Java applet NumberPadApplet that has the following layout and behavior:
Layout: The applet should consist of a text field "screen" (6 characters wide, initially containing the
number 0) and twelve buttons, as shown below. Note that by default, the contents of a text field are left
justified. Do not worry about colors, fonts, etc., when specifying the layout.
CLR 0 DEL
0 Screen
Buttons
Behavior: The applet should have the following behavior:
- Pressing a digit button should have the effect of extending the number in the screen with that digit as
a new last digit. This behavior is the one you expect in calculators, ATM machines, etc. Some
special cases:
(1) If the screen currently shows 0 then the pressed digit should replace 0.
(2) If the screen currently shows a number with 6 digits, pressing a digit should have no effect.
- Pressing the CLR button should erase any number in the screen and replace it by 0.
- Pressing the DEL button should delete the least significant digit of the currently displayed number.
E.g., if the screen shows 427, then after pressing DEL, the screen should show 42. If the screen
shows only a single digit, then pressing DEL makes it show 0.