15-100 Lecture 27 (Wednesday, March 30, 2005)

Intersection of two lists

In the last class we talked about unioning of lists. In this class, we'll do a similar task, which instead would be the intersection of two lists. An intersection of two sets is just the common elements within both lists. We're going to take the intersection of two collections, based upon an array.

If something is to be in the intersection set, it must be in both list. It is not sufficient for it to be in the first set, but it is necessary. One way of doing it is going through the first set, and check each of the elements to see if it is in the second set. If it is also in the second set, then add it to the array. If you get to the end of the second list and haven't found it, then don't do anything and move on to the next element. The easiest way to do that would be nested for loops, but it would be inefficient. If the list is ordered, then we can easily do a binary search. However, we can do better than that because we have even more information.

Let's say we have a list with the following elements: apple, bagel, boy, cat, dog, elephant. If we are at 'elephant' in the other list, we go through each element. We go through apple, bagel, etc, and eventually hit 'elephant'. Since both lists are ordered, if we hit elephant, we can't get an element smaller than elephant in that list, so we don't have to search apple, bagel, etc. again. So otherwise, we can always stop where we left off.

First we can do a trivial implementation. We'll make a static method first to get you used to static methods.

 

public static WordList intersection (WordList list1, WordList list2)
{
 //create the list to return.. what size would we need?  Since we're only doing an intersection, 
 //at most we would need a size of the smaller of the two.  
    WordList intersectionList;
    if (list1.nextSlot < list2.nextSlot)
       intersectionList = new WordList(list1.nextSlot);      
    else
       intersectionList = new WordList(list2.nextSlot);
       
    for (int index1 = 0; index1 < list1.nextSlot; index1++) {
        for (int index2 = 0; index2 < list2.nextSlot; index2++) { 
           if (list2.wordList[index2].compareTo(list1.wordList[index1]) == 0) {
              intersectionList.insert(list.wordList[index1]);
           }
        }
    }
    
    return intersectionList;
}

We'll introduce a syntax that you don't need to know for the next one. It's taken from C, a language that Java is based upon, and can shorten code significantly.

boolean ? (what to do if true) : (what to do if false)

In this problem we want to set the intersectionList's size to the smaller of the two list's sizes, so we would set our boolean to just be (list1.nextSlot < list2.nextSlot). If this is true, we set the new list's size to be list1.nextSlot, if false then list2.nextSlot. So we can initialize the list as follows:

WordList intersectionList =new WordList(((list1.nextSlot < list2.nextSlot) ? list1.nextSlot : list2.nextSlot)). Note that you don't need to know this, just saves a little bit of code.

One easy way to make it a little more efficient is to stop if you reach an element greater than the element you're at in the first list.



public static WordList intersection (WordList list1, WordList list2)
{
    //create the list to return.. what size would we need?  Since we're only doing an intersection, 
    //at most we would need a size of the smaller of the two.  
    WordList intersectionList = new WordList(((list1.nextSlot < list2.nextSlot) ? list1.nextSlot : list2.nextSlot));
       
    for (int index1 = 0; index1 < list1.nextSlot; index1++) {
        for (int index2 = 0; index2 < list2.nextSlot; index2++) { 
           int direction =  list2.wordList[index2].compareTo(list1.wordList[index1]);
           //if the word that we're looking for is greater than the word we're at.. then we can just exit
           if (direction > 0) {
              break;
           }
           //if it's equal than just add it
           if (direction == 0) { 
              intersectionList.insert(list.wordList[index1]);
           }
           //if it's less, then just continue. 
        }
    }
    
    return intersectionList;
}

If we want to pick up where we left off, we just need to declare our index2 outside of the for loop where we left off.

 

public static WordList intersection (WordList list1, WordList list2)
{  
    WordList intersectionList = new WordList(((list1.nextSlot < list2.nextSlot) ? list1.nextSlot : list2.nextSlot));
    int index2 = 0;
    for (int index1 = 0; index1 < list1.nextSlot; index1++) {
        for (; index2 < list2.nextSlot; index2++) { 
           int direction =  list2.wordList[index2].compareTo(list1.wordList[index1]);
           //if the word that we're looking for is greater than the word we're at.. then we can just exit
           if (direction > 0) {
              break;
           }
           //if it's equal than just add it
           if (direction == 0) { 
              intersectionList.insert(list.wordList[index1]);
           }
           //if it's less, then just continue. 
        }
    }
    
    return intersectionList;
}

We can speed up our search significantly by using binary search instead of with brute-force search, so we can make a helper method that would do it for us. private static int binSearch (WordList list, String word, int left, int right) { //count represents the number of items that we need to search, which is the right-left + 1 for (int count = (right-left) + 1; count > 0; count /= 2) { pivot = left + (right - left)/2; int direction = list.wordList[pivot].compareTo(word); //if it's just 0 if (direction == 0) return pivot; //if it's greater then just set the visit the left half, which would just change right to pivot - 1. if (direction > 0) right = pivot - 1; //otherwise check the right half, changing left to pivot + 1 if (direction < 0) left = pivot + 1; } return pivot; //if it gets here it's close to where it should leave off so just return that. }

Now in our intersection we can just check implement the method this way:


public static WordList intersection (WordList list1, WordList list2)
{
    WordList intersectionList = new WordList(((list1.nextSlot < list2.nextSlot) ? list1.nextSlot : list2.nextSlot));

    for (int index1 = 0; index1 < list1.nextSlot; index1++) {
        for (int index2 = 0; index2 < list2.nextSlot; index2++) { 
          index2 = binSearch(list2.wordList, list1.wordList[index1], index2, list2.nextSlot-1);
	  if (list1.wordList[index1].compareTo(list2.wordList[index2]) == 0)
	    intersectionList.add(list1.wordList[index1]);
        }
    }
    
    return intersectionList;
}