Module 3: Supplemental Material


Selection

Let's examine the code we wrote for finding the smallest element in an array:

    static int findSmallest (int[] A)
    {
        // Start by assuming first is smallest.
	int smallest = A[0];

        // Check against A[1], A[2] ... etc.
	for (int i=1; i < A.length; i++) {
	    if (A[i] < smallest) {
		smallest = A[i];
	    }
	}

	return smallest;
    }

Let's look at two variations: (source file)

    static int findSmallest2 (int[] A)
    {
        // Start with the largest possible int value.
	int smallest = Integer.MAX_VALUE;

        // Check against A[0], A[1], ...
	for (int i=0; i < A.length; i++) {
	    if (A[i] < smallest) {
		smallest = A[i];
	    }
	}

	return smallest;
    }



    static int findSmallest3 (int[] A)
    {
        // Robustness checks: check that array can be referenced.
        if ( (A == null) || (A.length == 0) ) {
            // ... need to take action ...
            // Return -1? System.exit?
        }
        

        // Start by assuming first is smallest.
	int smallest = A[0];

        // Check against A[1], A[2] ... etc.
	for (int i=1; i < A.length; i++) {
	    if (A[i] < smallest) {
		smallest = A[i];
	    }
	}

	return smallest;
    }
Note:

Exercise 1: Answer these questions:


Selection Sort

Recall the code for Selection-Sort (for arrays of int's): (source file)

    static void selectionSort (int[] A)
    {
        // We don't need to find the n-th smallest, so stop at n-1.
	for (int i=0; i < A.length-1; i++) {

	    // Find i-th smallest and swap.
	    int smallest = A[i];
	    int pos = i;

            // Look from i+1 and up.
	    for (int j=i+1; j < A.length; j++) {
		if (A[j] < smallest) {
		    smallest = A[j];
		    pos = j;
		}
	    }

	    // Swap into position i.
	    int temp = A[i];
	    A[i] = A[pos];
	    A[pos] = temp;

	}

Exercise 3: What would happen if the comparison

		if (A[j] < smallest) {
  
were changed to
		if (A[j] <= smallest) {
  
in the if-statement? Also, put the code for swapping in a separate swap method.

We've sorted 1D arrays so far. Can one sort a 2D array?

For fun, let's sort an image:

Here's the program: (source file)
import java.awt.*;
import java.awt.image.*;

public class SortPixels {

    public static void main (String[] argv)
    {
	ImageTool imTool = new ImageTool ();
	Image image = imTool.readImageFile ("FamousPerson.jpg");
	imTool.showImage (image, "Original");
	Image sortedImage = sort (image);
	imTool.showImage (sortedImage, "Sorted pixels");
    }
    
    static Image sort (Image image)
    {
	ImageTool imTool = new ImageTool ();
	int[][][] pixels = imTool.imageToPixels (image);

        // We'll do a selection sort.
	for (int i=0; i < pixels.length; i++) {
	    for (int j=0; j < pixels[i].length; j++) {

                // Find (i,j)-th minimum, using only the R value.
                int min = pixels[i][j][1];
                int pos1 = i, pos2 = j;

                // For i-th row, we have to go along row.
                for (int n=j+1; n < pixels[i].length; n++) {
                    if (pixels[i][n][1] < min) {
                        min = pixels[i][n][1];
                        pos1 = i;
                        pos2 = n;
                    }
                }
                
                // Now from row i+1 onwards.
                for (int m=i+1; m < pixels.length; m++) {
                    for (int n=0; n < pixels[m].length; n++) {
                        if (pixels[m][n][1] < min) {
                            min = pixels[m][n][1];
                            pos1 = m;
                            pos2 = n;
                        }
                    }
                }
                
                // Now swap into (i,j) place.
                for (int k=0; k < 4; k++) {
                    int temp = pixels[i][j][k];
                    pixels[i][j][k] = pixels[pos1][pos2][k];
                    pixels[pos1][pos2][k] = temp;
                }
                

	    } //end-for-jj

            // Because it runs slowly, we'll print updates ...
            System.out.println ("Finished row i=" + i);

	} //end-for-i

	return imTool.pixelsToImage (pixels);
    }

}

Exercise 3: Run the above program on this image. See if the sorted result makes sense. Can you identify the person in the image?

If that seemed confusing, let's try something simpler:

Here's the program: (source file)
public class RowSort {

    public static void main (String[] argv)
    {
        // Some test data:
        int[][] A = {
            {1, 0, 1},
            {1, 1, 1},
            {1, 0, 0},
            {0, 0, 0}
        };

        // Before:
        print (A);

        rowSort (A);

        // After:
        print (A);
    }


    static void rowSort (int[][] X)
    {
        // Simple Selection Sort. We are sorting only rows.
        for (int row=0; row < X.length; row++) {

            // This is the minimum so far.
            int min = rowSum (X[row]);
            int bestRow = row;

            // Scan remainder to see if there's a better one.
            for (int row2=row+1; row2 < X.length; row2++) {
                int sum = rowSum (X[row2]);
                if (sum < min) {
                    min = sum;
                    bestRow = row2;
                }
            }

            // This is a pointer swap: X[row] is NOT an int. It's a pointer to int[].
            int[] temp = X[row];
            X[row] = X[bestRow];
            X[bestRow] = temp;
        }
    }


    static int rowSum (int[] Y)
    {
        // Sum of elements of a row.
        int sum = 0;
        for (int i=0; i < Y.length; i++) {
            sum += Y[i];
        }
        return sum;
    }
    

    
    static void print (int[][] X)
    {
       // ... straightforward ...
    }

}
Note:

Exercise 4: Does the same idea work for sorting columns? Download ColumnSort.java and implement a column sort: sort the columns by column-sum (the sum of elements in a column).


Sorting objects

Let's return to the example where we sorted objects and make a few modifications:

Here's the (somewhat complicated) program: (source file)
// The class we define for "Person" objects:

class Person {

    // Some data:
    String firstName;
    String lastName;
    int birthYear;
    
    // We'll use this to decide what kind of sorting we want.
    public boolean sortByAge = false;


    // This is called a constructor:

    public Person (String firstName, String lastName, int birthYear)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.birthYear = birthYear;
    }


    // To allow for printing via System.out.println:

    public String toString ()
    {
        return "Person: " + firstName + " " + lastName + " (b. " + birthYear + ")";
    }
    


    // A comparison method to allow two Person instances to be compared.

    public boolean lessThan (Person p)
    {
        if (sortByAge) {
            // Compare ages.
            if (birthYear < p.birthYear) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            // Sort alphabetically by name, starting with lastname.
            if ( lastName.compareTo (p.lastName) < 0 ) {
                return true;
            }
            else if ( lastName.compareTo (p.lastName) > 0 ) {
                return false;
            }
            
            // If last names are equal, try first names.
            if ( firstName.compareTo (p.firstName) < 0 ) {
                return true;
            }
            else if ( firstName.compareTo (p.firstName) > 0 ) {
                return false;
            }

            // If both names are equal, use age.
            if (birthYear < p.birthYear) {
                return true;
            }
            else {
                return false;
            }
        }
    }

} //end-Person




public class ObjectSortExample2 {

    public static void main (String[] argv)
    {
        Person[] someKennedys = new Person [10];

        Person p = new Person ("Patrick", "Kennedy", 1858);

        System.out.println ("First one: " + p);
        someKennedys[0] = p;
        
        // Do the others:
        someKennedys[1] = new Person ("Joseph", "Kennedy", 1888);
        someKennedys[2] = new Person ("Rose", "Fitzgerald", 1890);
        someKennedys[3] = new Person ("Joseph", "Kennedy", 1915);
        someKennedys[4] = new Person ("John", "Kennedy", 1917);
        someKennedys[5] = new Person ("Jacqueline", "Bouvier", 1929);
        someKennedys[6] = new Person ("Robert", "Kennedy", 1925);
        someKennedys[7] = new Person ("Edward", "Kennedy", 1932);
        someKennedys[8] = new Person ("Caroline", "Kennedy", 1957);
        someKennedys[9] = new Person ("Maria", "Shriver", 1955);

        System.out.println ("Unsorted: ");
        print (someKennedys);

        System.out.println ("Sorted: ");
        sort (someKennedys);
        print (someKennedys);
        
        // We want to sort by age, so set that flag in all instances.
        for (int i=0; i < someKennedys.length; i++) {
            someKennedys[i].sortByAge = true;
        }

        System.out.println ("Sorted by age: ");
        sort (someKennedys);
        print (someKennedys);
        
    }



    static void print (Person[] people)
    {
        for (int i=0; i < people.length; i++) {
            // people[i] is a Person instance.
            System.out.println (people[i]);
        }
    }
    


    static void sort (Person[] people)
    {
        // Selection Sort.
	for (int i=0; i < people.length-1; i++) {

	    // Find i-th min and swap.
	    Person min = people[i];
	    int pos = i;

            // Scan the rest.

	    for (int j=i+1; j < people.length; j++) {
		if ( people[j].lessThan (min) ) {
		    min = people[j];
		    pos = j;
		}
	    }

	    // Swap.
	    Person temp = people[i];
	    people[i] = people[pos];
	    people[pos] = temp;
	}
    }
    
}
The are many things to note:
  • We've now added a flag to let us decide whether to sort by age or by name:
        // We'll use this to decide what kind of sorting we want.
        public boolean sortByAge = false;
        

  • Notice how a "constructor" is defined:
        public Person (String firstName, String lastName, int birthYear)
        {
            this.firstName = firstName;
            this.lastName = lastName;
            this.birthYear = birthYear;
        }
        
    • It's like a method (with parameters)
    • It must be public and cannot have a return type declared.
    • A constructor executes only once per instance when the instance is created.
    • There's a lot more to constructors; we've only introduced them here. See the advanced Java material for more details.

  • There's a special method defined called toString():
        public String toString ()
        {
            return "Person: " + firstName + " " + lastName + " (b. " + birthYear + ")";
        }
        
    • Java treats this a little differently.
    • A little later in main, you see the line
              Person p = new Person ("Patrick", "Kennedy", 1858);
              System.out.println ("First one: " + p);
             
    • Whenever an object is given to System.out.println() or invoked where there should be a String, Java automatically calls the object's toString() method.
    • This is useful for debugging, when we want to dump an object's contents to screen.
    • We don't have to write
                System.out.println (p.firstName);
                System.out.println (p.lastName);
                System.out.println (p.birthYear);
             
    • Instead, we write
                System.out.println (p);
             

  • Moving on (in main), we see that a sort-by-name occurs first. Then we set sortByAge=true in each instance, and sort again.

  • We use a simple Selection-Sort.

  • To compare two Person instances, we simply call the lessThan() method we defined for one of them, and pass the other as parameter:
                    // "min" is of type Person.
    		if ( people[j].lessThan (min) ) {
                        // ...
                    }
         

  • We needed a Person variable for the swap:
    	    // Swap.
    	    Person temp = people[i];
    	    people[i] = people[pos];
    	    people[pos] = temp;
         

Now for something more complicated (but more useful):

  • We will make our Person object satisfy the interface called Comparable in the Java library.
    class Person implements Comparable {
    
        // ...
    
    }
        

  • This requires us to implement a method called compareTo():
    class Person implements Comparable {
    
        // ...
    
        public int compareTo (Object d)
        {
            // ...
        }
    }
        

  • Why should we do this?
    • By making our class Person implement the Comparable, we are in effect saying that it can behave like a Comparable object.
    • The only behavior required of a Comparable object is to have the method
          public int compareTo (Object d)
          {
              // ...
          }
          
      implemented correctly.
    • Here, "correctly" means: proper comparison of Person instances.
    • Once this is done, we can take advantage of any sorting algorithm written for Comparable objects.
    • For example, Java's own sorting algorithm in the Arrays class:
            public static void sort (Object[] A)
            

  • The code below uses Java's sorting algorithm.
Here's the program: (source file)
import java.util.*;

class Person implements Comparable<Person> {

    // Some data:
    String firstName;
    String lastName;
    int birthYear;
    
    // We'll use this to decide what kind of sorting we want.
    public boolean sortByAge = false;


    // This is called a constructor:

    public Person (String firstName, String lastName, int birthYear)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.birthYear = birthYear;
    }


    // To allow for printing via System.out.println:

    public String toString ()
    {
        return "Person: " + firstName + " " + lastName + " (b. " + birthYear + ")";
    }
    


    // A comparison method to allow two Person instances to be compared.

    public int compareTo (Person d)
    {
        // Check if object passed in is of correct type.
        if (! (d instanceof Person) ) {
            return -1;
        }
        
        // Cast to a Person variable so that we can access fields.
        Person p = (Person) d;

        if (sortByAge) {
            // Compare ages.
            if (birthYear < p.birthYear) {
                return -1;
            }
            else if (birthYear > p.birthYear) {
                return 1;
            }
            else {
                return 0;
            }
            
        }
        else {
            // Sort alphabetically by name, starting with lastname.
            if ( lastName.compareTo (p.lastName) < 0 ) {
                return -1;
            }
            else if ( lastName.compareTo (p.lastName) > 0 ) {
                return 1;
            }
            
            // If last names are equal, try first names.
            if ( firstName.compareTo (p.firstName) < 0 ) {
                return -1;
            }
            else if ( firstName.compareTo (p.firstName) > 0 ) {
                return 1;
            }
            else {
                return 0;
            }
        }
    }

} //end-Person




public class ObjectSortExample4 {

    public static void main (String[] argv)
    {
        Person[] someKennedys = new Person [10];

        Person p = new Person ("Patrick", "Kennedy", 1858);

        System.out.println ("First one: " + p);
        someKennedys[0] = p;
        
        // Do the others:
        someKennedys[1] = new Person ("Joseph", "Kennedy", 1888);
        someKennedys[2] = new Person ("Rose", "Fitzgerald", 1890);
        someKennedys[3] = new Person ("Joseph", "Kennedy", 1915);
        someKennedys[4] = new Person ("John", "Kennedy", 1917);
        someKennedys[5] = new Person ("Jacqueline", "Bouvier", 1929);
        someKennedys[6] = new Person ("Robert", "Kennedy", 1925);
        someKennedys[7] = new Person ("Edward", "Kennedy", 1932);
        someKennedys[8] = new Person ("Caroline", "Kennedy", 1957);
        someKennedys[9] = new Person ("Maria", "Shriver", 1955);

        System.out.println ("Unsorted: ");
        print (someKennedys);

        System.out.println ("Sorted: ");
        // Java's sort method.
        Arrays.sort (someKennedys);
        print (someKennedys);
        
        // We want to sort by age, so set that flag in all instances.
        for (int i=0; i < someKennedys.length; i++) {
            someKennedys[i].sortByAge = true;
        }

        System.out.println ("Sorted by age: ");
        // Java's sort method:
        Arrays.sort (someKennedys);
        print (someKennedys);
        
    }



    static void print (Person[] people)
    {
        // ...
    }
    
}