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

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

|   | |   |    |   | |   |     | 2 | |   |    |   | |   |     |   | |   |
|   | |   |    |   | |   |     | 3 | | * |    | 6 | |   |     | 6 | |   |
| 2 | |   |    |   | |   |     |24 | | / |    |24 | | / |     |24 | | / |
| 7 | | - |    | 5 | |   |     | 5 | | + |    | 5 | | + |     | 5 | | + |
+---+ +---+    +---+ +---+     +---+ +---+    +---+ +---+     +---+ +---+
before 1st )   after 1st )     before 2nd )   after 2nd )     before 3rd )


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


2. The only way to initialize variables k and m so as to "truthify" the loop invariant is by setting both to zero. Yet more than a couple of students got that wrong. Suppose, for example, that k is initialized to zero and m is initialized to N. Plugging in zero for k and N for m in the loop invariant yields this statement:

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

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

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

swap(A, A[k], A[N-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

   k = 0 ;  m = 0 ;  // initialize k and m to truthify
                     // the loop invariant

   // loop invariant: 0<=k<=m<=N  &&
   //   every element in A[0..k) is RED  &&
   //   every element in A[k..m) is BLUE

   while ( m != N )  {

      if (isRed(A[N-1])) {

         swap(A, k, m);   //alternative: swap(A, m, N-1);
         swap(A, k, N-1); //             swap(A, k, m);
         k = k+1;
         m = m+1;

      }
      else {   // A[N-1] is blue

         swap(A, m, N-1);
         m = m+1;

      }

   }
   // post: 0<=k<=N  &&
   //       every element in A[0..k) is RED  &&
   //       every element in A[k..N) is BLUE
}

A common, and subtle, mistake made in the case that A[N−1] was RED was to perform this sequence of swaps:

swap(A, k, N-1); swap(A, m, N-1);

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

This works, except in the case that k == m, corresponding to the BLUE 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] happened to be BLUE, the increment of k (after the two swaps, which have no net effect) results in A[k-1] containing a BLUE element, falsifying the second conjunct of the loop invariant.

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

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

if (k != m) { swap(A, m, N-1); }


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

 Enqueued at    1    3    4    6    8    9   12   13   14   18   19

 Dequeued at    2    5    7   10   11   15   16   17   20   21   22


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


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);
}

/* Moves this date backwards by one month
** E.g., 3/15 becomes 2/15; 5/31 becomes 4/30
*/
public void goBackByOneMonth() {
   if (month == 1) {
      month = MONTHS_PER_YEAR;
      year = year - 1;
   }
   else {
      month = month - 1;
   }
   day = Math.min(day, daysInMonth(year, month);
}
/* Moves this date backwards by one month
** E.g., 3/15 becomes 2/15; 5/31 becomes 4/30
*/
public void goBackByOneMonth() {
   int y;
   int m = monthOf() - 1;
   if (m == 0) {
      m = MONTHS_PER_YEAR;
      y = yearOf() - 1;
   }
   else {
      y = yearOf();
   }
   int d = Math.min(dayOf(), daysInMonth(y,m));
   setTo(y, m, d);
}


5.
public class BookSorterByPages extends BookSorter {

   protected boolean lessThan(Book book1, Book book2) {
      return book1.numPages < book2.numPages;
   }
}
public class BookSorterByAuthor extends BookSorter {

   protected boolean lessThan(Book book1, Book book2) {
      String name1 = book1.authorName;
      String name2 = book2.authorName;
      return name1.compareTo(name2) < 0;
   }
}