CMPS 144 Fall 2023
Prog. Assg. #4: (Recursive) Split/Coalesce Sorting
Due: 11:59pm, December 3

One of the most important, and well-studied, problems in computing is that of sorting. It comes in various guises, but essentially the problem is this: Given a collection of values of some type —and a total ordering1 on that type— produce a list/sequence of those values in ascending order (from "smallest" to "largest").

For this assignment, the focus is on sorting an array using a particular recursive sorting algorithm that we will call Split/Coalesce Sort.

Expressed at a slightly higher level than Java code, one version of the Split/Coalesce algorithm to sort the array segment a[low..high) goes like this:

As an example, suppose that the situation is as depicted here:

              |low                 mid                    |high
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
a |    ...    | 8 | 3 |17 | 4 |11 | 5 | 0 |21 | 2 | 3 | 9 |    ...    |
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+

Step 1 computes mid to be where it is shown in the diagram above.

Steps 2 and 3 establish this:

           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 8 | 3 |17 | 4 |11 |
         +---+---+---+---+---+
           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 3 | 4 | 8 |11 |17 |
         +---+---+---+---+---+
After step 2 After step 3

Steps 4 and 5 establish this:

            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 5 | 0 |21 | 2 | 3 | 9 |
          +---+---+---+---+---+---+
            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 0 | 2 | 3 | 5 | 9 |21 |
          +---+---+---+---+---+---+
After step 4 After step 5

Step 6: Coalescing

The most complicated part of the algorithm is Step 6, in which the elements of leftHalf[] and rightHalf[] are coalesced. Below we illustrate the effects of the first five iterations of that process when applied to our ongoing example. The elements of a[low..high) are shown as -'s because they will all be overwritten during the coalescing process, without regard to whatever values are stored there.

Initially
           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 3 | 4 | 8 |11 |17 |
         +---+---+---+---+---+
           i
            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 0 | 2 | 3 | 5 | 9 |21 |
          +---+---+---+---+---+---+
            j
              |low                                        |high
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
a |    ...    | - | - | - | - | - | - | - | - | - | - | - |    ...    |
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
                k 

After one iteration
           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 3 | 4 | 8 |11 |17 |
         +---+---+---+---+---+
           i
            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 0 | 2 | 3 | 5 | 9 |21 |
          +---+---+---+---+---+---+
                j
              |low                                        |high
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
a |    ...    | 0 | - | - | - | - | - | - | - | - | - | - |    ...    |
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
                    k 

After two iterations
           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 3 | 4 | 8 |11 |17 |
         +---+---+---+---+---+
           i
            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 0 | 2 | 3 | 5 | 9 |21 |
          +---+---+---+---+---+---+
                    j
              |low                                        |high
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
a |    ...    | 0 | 2 | - | - | - | - | - | - | - | - | - |    ...    |
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
                        k 

After three iterations
           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 3 | 4 | 8 |11 |17 |
         +---+---+---+---+---+
               i
            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 0 | 2 | 3 | 5 | 9 |21 |
          +---+---+---+---+---+---+
                    j
              |low                                        |high
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
a |    ...    | 0 | 2 | 3 | - | - | - | - | - | - | - | - |    ...    |
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
                            k 

After four iterations
           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 3 | 4 | 8 |11 |17 |
         +---+---+---+---+---+
               i
            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 0 | 2 | 3 | 5 | 9 |21 |
          +---+---+---+---+---+---+
                        j
              |low                                        |high
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
a |    ...    | 0 | 2 | 3 | 3 | - | - | - | - | - | - | - |    ...    |
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
                                k 

After five iterations
           0   1   2   3   4 
         +---+---+---+---+---+
leftHalf | 3 | 4 | 8 |11 |17 |
         +---+---+---+---+---+
                   i
            0   1   2   3   4   5
          +---+---+---+---+---+---+
rightHalf | 0 | 2 | 3 | 5 | 9 |21 |
          +---+---+---+---+---+---+
                        j
              |low                                        |high
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
a |    ...    | 0 | 2 | 3 | 3 | 4 | - | - | - | - | - | - |    ...    |
  +-----------+---+---+---+---+---+---+---+---+---+---+---+-----------+
                                    k 

What the reader should have inferred from this sequence of diagrams is that during each iteration of Step 6 (the "coalescing" process), the values leftHalf[i] and rightHalf[j] are compared and the smaller of the two is copied into (i.e., assigned to) a[k]. (In case of a tie, as occurred during the third iteration, it does not matter which one we choose.) Of course, k increases by one, as does one among i and j (according to whether the smaller value was leftHalf[i] or rightHalf[j] respectively).

The loop invariant is this:

  1. k = low + i + j (hence variable k is not really needed) and
  2. a[low..k) contains, in ascending order, all the values in leftHalf[0..i) and rightHalf[0..j).

Of course, at some point during the coalescing process, either i will become equal to leftHalf.length or j will become equal to rightHalf.length. In the former (respectively, latter) case, rightHalf[j..rightHalf.length) (respectively leftHalf[i..leftHalf.length)) should be copied into a[k..high).

Step 6 Revisited: Coalescing via Recursion

To frame the problem, suppose that we have array suffixes sourceA[i..M) and sourceB[j..N), the elements of which are assumed to be in ascending order, and we want to "coalesce" those elements into dest[k .. k+(M-i)+(N-j)), where M (respectively, N) is shorthand for sourceA.length (respectively, sourceB.length).

  1. In the case i = M, all that needs to be done is to copy sourceB[j..N) into dest[k .. k+(N-j)).
  2. In the case j = N, all that needs to be done is to copy sourceA[i..M) into dest[k .. k+(M-i)).
  3. In the case that i < M and j < N, there are two sub-cases:
    1. If sourceA[i] ≤ sourceB[j], then sourceA[i] should be copied into (i.e., assigned to) dest[k]. What remains to be done is to (recursively) coalesce sourceA[i+1..M) and sourceB[j..N) into dest[k+1 .. k+(M-i-1)+(N-j)).
    2. If sourceA[i] > sourceB[j], then sourceB[j] should be copied into (i.e., assigned to) dest[k]. What remains to be done is to (recursively) coalesce sourceA[i..M) and sourceB[j+1..N) into dest[k+1 .. k+(M-i)+(N-j-1)).


The Student's Task

The following Java interfaces/classes are provided:

Addition/Optional:
Students having sufficient ambition are encouraged, after they have completed a correct version of SplitCoalesceSorter_int, to modify it so as to produce a generic version of that class, capable of sorting arrays containing values of any (reference) type, consistent with a total ordering defined by a Comparator (i.e., an instance of a class that implements the java.util.Comparator interface) that was provided to the constructor.

Relevant Java artifacts to support the student's endeavor are these:


Copying Array Elements

Steps 2 and 4 of the Split/Coalesce sorting algorithm involve creating a new array and filling it with the values in a segment of another array. A good way of doing that is to make use of the copyOfRange() method in the java.util.Arrays class. As an example, Step 2 of the algorithm could translate into the Java statement

int[] leftHalf = Arrays.copyOfRange(a, low, mid);

Note: In the generic version of the class, the same statement will work, except that leftHalf[] would be declared to be of type T[] rather than int[]. End of note.

The algorithm by which to coalesce two arrays into the segment of another array includes a step that involves copying the elements of one array segment into another one. As an example, suppose that the elements of leftHalf[i..M) are to be copied into a[k .. k+M-i). This is accomplished using the System.arraycopy() method, as follows:

System.arraycopy(leftHalf, i, a, k, M-i);

The parameters are as follows:

  1. The source array (from which values are to be copied)
  2. The location in the source array at which the "giving" segment begins
  3. The destination array
  4. The location in the destination array at which the "receiving" segment begins
  5. The length of the "giving" and "receiving" segments

Program Submission

Submit (to the appropriate Brightspace dropbox) the relevant source code, which is that in the file SplitCoalesceSorter_int.java. If you also completed the generic version of the class (or made a reasonable attempt), submit SplitCoalesceSorter.java, too.



Footnote

[1] A total ordering on a type T is a binary relation ≤ ⊆ T×T satisfying these properties:
  1. It is reflexive, meaning that, for all x∈T, x≤x.
  2. For all x,y ∈ T such that x≠y, exactly one among x≤y and y≤x holds.
  3. It is transitive, meaning that, for every x,y,z ∈ T, if x≤y and y≤z, then x≤z.