CMPS 144 Spring 2024
Test #1 Sample Solutions (Section 1)

1. The FPAE to be evaluated was (((7 - 2) + 3) * ((20 / 4) + 1)).

|   | |   |    |   | |   |     |   | |   |    |   | |   |     |   | |   |
|   | |   |    |   | |   |     |   | |   |    |   | |   |     | 4 | |   |
| 2 | |   |    |   | |   |     | 3 | |   |    |   | |   |     |20 | | / |
| 7 | | - |    | 5 | |   |     | 5 | | + |    | 8 | |   |     | 8 | | * |
+---+ +---+    +---+ +---+     +---+ +---+    +---+ +---+     +---+ +---+
before 1st )   after 1st )     before 2nd )   after 2nd )     before 3rd )


|   | |   |    |   | |   |     |   | |   |    |   | |   |     |   | |   |
|   | |   |    | 1 | |   |     |   | |   |    |   | |   |     |   | |   |
| 5 | |   |    | 5 | | + |     | 6 | |   |    | 6 | |   |     |   | |   |
| 8 | | * |    | 8 | | * |     | 8 | | * |    | 8 | | * |     |48 | |   |
+---+ +---+    +---+ +---+     +---+ +---+    +---+ +---+     +---+ +---+
after 3rd )    before 4th )    after 4th )    before 5th )    after 5th )


2. The only way to initialize variables j and k so as to "truthify" the loop invariant is by setting both to N (shorthand for A.length). Yet several students got that wrong. Suppose, for example, that j is initialized to zero and k is initialized to N. Plugging in zero for j and N for k in the loop invariant yields this statement:

0≤0≤N≤N  ∧  every element in A[0..N) is RED  ∧  every element in A[N..N) is BLUE

Granted, the first and third conjuncts are clearly true, but the second is false (except in the unusual case of the array containing no BLUE elements).

Another common mistake was to pass the values of array elements to the swap() method, as in

swap(A, A[0], A[j-1]);
The swap() method expects to receive, via its second and third formal parameters, the indices/locations of the two elements to swap, not the values of those elements. To fail to make the distinction between the contents of a memory location and its address is a mistake frequently made by novice programmers.

Here is a correct solution:

// precondition: every element in A[0..N) is either RED or BLUE

   j = N ;  k = N ;  // initialize j and k to truthify
                     // the loop invariant

   // loop invariant: 0≤j≤k≤N  ∧
   //   every element in A[j..k) is RED  ∧
   //   every element in A[k..N) is BLUE

   while ( j != 0 )  {

      if (isRed(A[0])) {

         j = j-1;
         swap(A, 0, j); 

      }
      else {   // A[0] is blue
         j = j-1;
         k = k-1;
         swap(A, j, k);   //alternative: swap(A, 0, j);
         swap(A, 0, k);   //             swap(A, j, k);

      }

   }
   // post: 0≤k≤N  ∧
   //       every element in A[j..k) is RED  ∧
   //       every element in A[k..N) is BLUE
}

A common, and subtle, mistake made in the case that A[0] was BLUE was to perform this sequence of swaps and assignments:

swap(A, 0, k-1); swap(A, 0, j-1);
j = j-1; k = k-1;

The first swap places the BLUE element from location 0 into location k-1, thereby extending the BLUE segment to include location k-1. The intent is for the second swap to place the RED element that had been at location k-1 (before it was swapped into location 0 by the first swap) into location j-1, the overall effect being to shift the RED segment by one place so as to include location j-1.

This works, except in the case that j == k, corresponding to the RED segment being empty. In that case, the two swaps involve the same pair of locations, and so the second one undoes the effect of the first. Which means that, if A[k-1] happened to be RED, the decrement of k (after the two swaps, which have no net effect) results in A[k] containing a RED element, falsifying the third conjunct of the loop invariant.

As a concrete example, consider the first iteration of the loop, with both j and k having value N and A[0] being BLUE and A[N-1] being RED. At the conclusion of that iteration, k == N-1 but A[k] remains RED.

A fix for this anomaly would be to refrain from performing the second swap in the case that j == k. That would be achieved by "guarding" it within an if-else statement, like this:

if (j != k) { swap(A, 0, j-1); }


3.
History of the queue:
             +----+----+----+----+----+----+----+----+----+----+----+----+
             |  2 |  7 |  8 |  4 |  9 |  3 |  6 |  5 |  1 | 11 |  0 |    | 
             +----+----+----+----+----+----+----+----+----+----+----+----+

 Enqueued at    1    3    4    6    7    9   10   13   15   16   19

 Dequeued at    2    5    8   11   12   14   17   18   20   21   22


Distance array:
        0    1    2    3    4    5    6    7    8    9   10   11
     +----+----+----+----+----+----+----+----+----+----+----+----+
dist |  4 |  3 |  0 |  2 |  2 |  3 |  2 |  1 |  1 |  2 | -1 |  3 |
     +----+----+----+----+----+----+----+----+----+----+----+----+
Breadth-first search tree:
        2 
       / \
      /   \
     /     \
    /       \
   7         8
  / \       / \
 4   9     3   6
     |    / \
     5   1   11
     |
     0


4. The instructions explicitly said that the two methods were intended to be mutators, as opposed to observers. The methods' names also suggested that intent. Yet a number of students wrote method bodies that included return statements and/or whose execution could not possibly modify the values of any of the object's instance variables, as though the methods were observers.

A much less egregious mistake made by many students was to make direct reference to the DAYS_IN_MONTH[] array, even though the provided daysInMonth() method provides better information (because it yields the correct number of days for a leap year February).

Provided are two correct solutions for each method. The ones on the right avoid direct references to instance variables.

/* Advances this date to the end of the month.
** E.g., 3/15 becomes 3/31; 4/11 becomes 4/30.
*/
public void advanceToEndOfMonth() {
   day = daysInMonth(year, month);
}
/* Advances this date to the end of the month.
** E.g., 3/15 becomes 3/31; 4/11 becomes 4/30.
*/
public void advanceToEndOfMonth() {
   int lastDay = daysInMonth(yearOf(), monthOf());
   setTo(yearOf(), monthOf(), lastDay);
}

/* Advances this date by one month
** E.g., 3/15 becomes 4/15; 3/31 becomes 4/30
*/
public void advanceByOneMonth() {
   if (month == MONTHS_PER_YEAR) {
      month = 1;
      year = year + 1;
   }
   else {
      month = month + 1;
   }
   day = Math.min(day, daysInMonth(year, month);
}
/* Advances this date by one month
** E.g., 3/15 becomes 4/15; 3/31 becomes 4/30
*/
public void advanceByOneMonth() {
   int y;
   int m = monthOf() + 1;
   if (m > MONTHS_PER_YEAR) {
      m = 1;
      y = yearOf() + 1;
   }
   else {
      y = yearOf();
   }
   int d = Math.min(dayOf(), daysInMonth(y,m));
   setTo(y, m, d);
}


5.
public class AutoSorterByMake extends AutomobileSorter {

   protected boolean lessThan(Automobile auto1, Automobile auto2) {
      String make1 = auto1.make;
      String make2 = auto2.make;
      return make1.compareTo(make2) < 0;
   }
}
public class AutoSorterByYear extends AutomobileSorter {

   protected boolean lessThan(Automobile auto1, Automobile auto2) {
      return auto1.year < auto2.year;
   }
}