return to lecture notes index

15-100 Lecture 26 (Monday, March 28, 2005)

Old Code


class Set {
  private Comparable[] members; //using an array of Comparable means that any object of a class that 
                        //implements Comparable can be placed in this array
  private int count;
  
  private static final int DEFAULT_LENGTH = 10; //reminder: static means this is a variable that
                       //is a constant (its final) that is part of the class specification but 
                       //not part of the objects that this class makes
  private static final String NL = System.getProperty("line_separator");
  private int memberLocation; //conatains the location of the last found isMember
  
  public Set() [
    members = new Comparable[DEFAULT_LENGTH];
    count = 0;
    memberLocation = -1;
  }
  
  public Set(int length){
    members = new Comparable[length];
    count = 0;
    memberLocation = -1;
  }
  
  public void add(Comparable newItem){
    //Should do this check first because don't want to do anything if can't add newItem
    if (isMember(newItem)) //keeping a set so don't want to allow duplicates, exit method
        return;
    
    if (count == members.length)
      grow();
      
    int index; //has scope outside for loop
    for (index = count; index > 0; index--){     
      int difference = members[index-1].compareTo(c);
      //note: difference should never equal 0
      if (difference > 0)
        members[index] = members[index-1];
      if (difference < 0)
        break;
    }
    
    members[index] = newItem;
  }
  
  private void grow() {
    Comparable[] biggerArray = new Comparable[2*members.length];
    
    for (int index = 0; index < count; index++){
      biggerArray[index] = members[index];
    }
    
    members = biggerArray;
  }
  
  //this does the same thing as the isMember we wrote in the last class
  public boolean isMember(Comparable item) {
    return isMember(Comparable item, 0);
  }
  
  //modified isMember so that can shrink the size of the set it has to search
  private boolean isMember(Comparable item, int initalLeft) {
    int left = initalLeft;
    int right = count-1;
    int pivot = left + (right-left)/2;
    
    while(right >= left) {
      int difference = members[pivot].compareTo(item);
      
      if (difference == 0) {
        memberLocation = pivot;
        return true;
      }
      if (difference > 0)
        right = pivot-1;
      if (difference < 0){
       left = pivot +1;
       memberLocation = pivot;
      }
       
      pivot = left + (right-left)/2  
    }
    
    if (memberLoction < 0)
     memberLocation = 0;
    return false;
  }
  
  /////////////////////NEW CODE//////////////////////

  public String toString() {
    String retString = "";
    //simple transversal of the array...adding each object into the retString
    for (int index = 0; index < count; index++) {
      //assuming that all objects in array have overridden the toString method
      retString += members[index] + NL; 
    }
    
    return retString;
  }
Unions

Remember from math that the union of two sets consists of all objects that appear in either of the two sets (and since they are sets their are no repeats of the objects.) So, our union takes an array (to create a union with the main array in the class) and returns a new Set that contains all elements that are in either of the two arrays (while avoiding repeating/ duplicting an object). A way you can do this is to copy the first set then iterate through the second set and add to the copied set all objects that aren't members of the copied set already.

Note that our add method all ready checks for duplicates so the easiest code would be:


  public Set union(Set otherSet) {
    Set unionSet = new Set (this);//this is a copy constructor that makes a copy of
                                  //the Set.
    for (int index = 0; index < count; index++) {
      unionSet.add(otherSet.members[index]);
    }
  }
  
  //our copy constructor...
  public Set(Set sourceSet) {
    members = new Comparable [sourceSet.members.length] //this will get the length of the 
               //sourceSet's member array (even though member is private we can do this 
               //because private instance variables can be accessed by other objects of the
               //same class.
    count = sourceSet.count;
    memberLocation = -1;
    
    //now copy all of the objects into our new array (simple transversal)
    for (int index = 0; index < count; index++) {
      members[index] = sourceSet.members[index];
    }
  }
However, if we assume that we have an add method that looks like this:

  public void alwaysAdd(Comparable newItem){
    //note lack of check for duplicates
    
    if (count == members.length)
      grow();
      
    int index; //has scope outside for loop
    for (index = count; index > 0; index--){     
      int difference = members[index-1].compareTo(c);
      //note: difference should never equal 0..otherwise you have passes in a duplicate
      if (difference > 0)
        members[index] = members[index-1];
      if (difference < 0)
        break;
    }
    
    members[index] = newItem;
  }
We would have to call isMember() before adding the new object. Like so:

  public Set union(Set otherSet) {
    Set unionSet = new Set (this);//this is a copy constructor that makes a copy of
                                  //the Set.
    for (int index = 0; index < count; index++) {
      if (! unionSet.isMember(otherSet.members[index]))
        unionSet.alwaysAdd(otherSet.members[index]);
    }
  }
Modifying a Binary Search to make an efficent union()
This code will do the correct job. However, it isn't the best that we can do. A binary search is the best way to look for an element (assuming that we are looking for a random element in an ordered array). Since we are looking at two sets that are both ordered we can do better than a binary search.

You can go through the lists at the same time, keeping in mind that since they are ordered the next number that you are looking for won't be prior to the last possible contender for the last search. As you go through the sets compare one set to the other by keeping a referance in each of the sets which refers to the numbers you just compared, and continue searching as long as a variable's referance is less than the other's (aka less than the value being searched for).

You can also do a binary search on the elements left over in the second set after the previous search. Since a binary search is the quickest way to search a set this is the quickest way to search (as long as you don't always start back at the begining). So keeping in mind that the sets are ordered the most efficent search would be a binary search limited by the previous left bound (or where the last member was found to be equal). You should never start earlier than where you left off!

With that in mind go back to the top of the lecture and relook at isMember(). It has been modified so that it stores what the last left was (and if it returned true then memberLocation is the pivot or points to where the equal memeber was) in a new instance variable memberLocation (so it has scope in all of the methods in our program). memeberLocation is the key to shortening our search time. Because memberLocation always points to an object less than or equal to what we were last looking for memberLocation is where we should start our search. (Notice that memberLocation treads on the side of caution and is typically one less than the last left bound...allowing for the margin of error when right crosses left).


  public Set union(Set otherSet) {
    Set unionSet = new Set (this);
    
    for (int index = 0; index < count; index++) {
      if (! unionSet.isMember(otherSet.members[index], memberLocation))
        unionSet.alwaysAdd(otherSet.members[index]);
    }
  }
}