CS62 - Spring 2021 - Class 7

Example code in this lecture

   BinarySearchExamples
   GenericsExamples

Lecture notes

  • admin
       - Darwin part 1 (World and Species) due tonight
       
  • look at search1 method in BinarySearchExamples code
       - what does this method do?
       - what is the running time, using Big-O notation?
          - depends! sometimes an method/approach always has the same running time
             - look at search1a method in BinarySearchExamples code
          - In general, we'll talk about three different things for a method:
             - best case running time
             - worst case running time
             - average case running time
       - what are the best, worst and average case running times
          - best: O(1), constant, when the example is the first element
          - worse: O(n), linear, when the example is the last element
          - average: O(n), linear, on average, we'll need to traverse half of the elements (~n/2) which is still O(n)

  • look at search2 method in BinarySearchExamples code
       - what does this method do?
          - uses a helper method
          - does the same thing as the previous, but using recursion
       - which version is better?
       - what is the running time, using Big-O notation?
          - same as for the iterative version

  • can we do better than either of these procedures?
       - without any preprocessing of the data, no

  • Last time we played the number guessing game. How were you able to find the number without just listing a bunch of random numbers?
       - When we guessed a number, we were told larger or smaller which allowed us to eliminate a bunch of numbers   

  • what if I told you the data was in sorted order?
       - how do you find information in a phonebook (where the data is in essence, sorted)?

  • show binarySearch method in BinarySearchExamples code
       - what does the code do?
          - keeps a low and a high value
          - we know that if findMe is in the array, then nums[low] <= findMe <= nums[high]
          - picks the middle element between low and high
          - compares that middle element to our value and then either finds the data or DISCARDS HALF OF THE REMAINING DATA
       - an example
          - 1, 3, 7, 15, 16, 18, 21, 40, 45, 50
       - what's the running-time?
          - best case: O(1) it's the midpoint in the array
          - worst case: not found
             - how many times do we iterate through the while loop?
             - let's consider the case where the number of elements is a power of 2
                - we could always pad it up do the next largest power of 2
             - at each iteration we throw away half of the data, n/2, n/4, n/8
             - when will it be done?
                - when n/2^i = 1
                log n/2^i = log 1
                log n - log 2^i = 0
                log n - 2 log i = 0
                log n = 2 log i
                i = log_2 n
             - runtime is O(log_2 n)
          - average case: half as many iterations through the while loop... still O(log_2 n)

  • how would we write a recursive version?
       - we'd need a helper method
       - rather than keeping low and high as variables, they'll be parameters
       - then, rather than adjust them, we just call recursively call our method with smaller values

  • show binarySearchRecursive in BinarySearchExamples code

  • Last comments on binary search
       - It‚ is easy to get indices wrong, so be careful!
          - "Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky... Professor Donald Knuth (taken from http://en.wikipedia.org/wiki/Binary_search_algorithm)
       - http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html
       - How hard is it? http://portal.acm.org/citation.cfm?doid=358476.358484, only accessible on campus
       - What if we wanted to return the first one in the list?


  • Generics
       - We can write our own classes that use generics!
       - Look at Container class in GenericsExamples code
          - We can specify one or my type variables in the class header inside < >
             public class Container<V>{
       
             }

             - by convention, we use a single uppercase letter to indicate a type variable
          - You can then use this variable anywhere in your program where a type would be used
             - private V value;
             - public Container(V value)
             - public V getValue()
             - public void setValue(V newValue)
       - When you instantiate this class you should specify a type parameter
          Container<Integer> c = new Container<Integer>();
          int val = c.getValue();

          or

          Container<String> c = new Container<String>();
          String val = c.getValue();

          - For example, we could add the main:

             Container<String> c1 = new Container<String>("banana");
             Container<Integer> c2 = new Container<Integer>(10);
          
             System.out.println(c1.getValue());
             System.out.println(c2.getValue());
          
             // c2.setValue("car"); // DOESN'T WORK!

       - How can we return multiple objects/data types from a method in java?
          - The bad way: public void Object[] method()
          - The good way: implement a Pair class using generics

       - implement a class called Pair that supports generics for 2 types
          - if you need more than one type variable, you can introduce another one and comma separate
          - What methods should the class contain?
          - Constructor(s)?
          - private instance variables?
          - methods?

       - look at the Pair class in the GenericsExamples code