CS62 - Fall 2026 - Class 6

Example code in this lecture

   AsymptoticsExamples
   BinarySearchExamples

Lecture notes

  • admin
       - Darwin part 1 (World and Species)
          - Due Tuesday

       - Where we are
          - covered most of the "big" new things in Java
          - We'll continue to introduce a few random Java things each class, but we will now transition to the data structures side of things

  • type casting
       - If we have a class B extends A, is the following legal?
          A varA = new B(...);

          - yes, we can always assign a subclass to a variable of the type of a parent class

       - Could we then do the following?
          B varB = varA;

          - No! Even though in this case we know that there is a B in varA, the Java compiler cannot be sure in all cases

       - However, sometimes we (as the programmer) know the contents. We can tell Java that we know the type and to cast (interpret) the value as that type.
       - The way to do that is with parentheses and the type:

          B varB = (B)varA

       - We can also do this with some of the built-in types:
          double y = 5.5;
          int x = (int)y;

          - Note that this will truncate the decimal part.

       - We can also use type casting to force Java to do floating point division rather than integer division
          - The WRONG way:
          int x = 9;
          int y = 2;
          double z = x/y;

          - The right way
          int x = 9;
          int y = 2;
          double z = ((double)x)/y;

  • this(...)
       - We'll often have multiple constructors in a class. Why?
          - Allow for different ways of creating the object
          - Often, allow for having versions that specify more details

       - It can be convenient in these cases to call one of the constructors from another constructor
          - there's a special syntax to do this because we can just say new ..., since that would create a whole new instance of the class

       - Look at the Matrix class from Darwin
          - We have two constructors
          - The first one simply calls the second one with the default parameters

       - the "this" call (like a super call), must be the first line in the constructor

  • char
       - one of the other built in types for representing a single character
       - to create them use single quotes
          char c = 'a';

       - returned by some of the String methods, e.g.
          String s = "...";
          char first = s.charAt(0);

  • look at the sum method in AsymptoticsExamples code
       - How long will it take if we pass it an array with 1000 numbers in it? 10,000 numbers?
          - We don't know!
          - We could time it and find out
       - Even if you time it, can you say conclusively how long it will take?
          - No!
          - Variation from run to run
          - Depends on the computer
          - etc.
       - If I tell you the time it took on 10,000 numbers was t. Could you tell me approximately how long it would take on 20,000 numbers?
          - would take about twice as long, i.e., 2t
          - why?
             - does a linear pass through the data
             - doubling the size of the data, means about twice as much work

  • Asymptotics
       - Precisely calculating the actual cost of a method is tedious and not generally useful
       - Different operations take different amounts of time. Even from run to run, things such as caching, etc. will complicate things
       - Want to identify categories of algorithmic runtimes
       - What we really want to do is compare different algorithms
          - Want to know which algorithms will be much worse (or much better) than others

  • Big O
       - We write that an algorithm's run-time is O(g(n)) if the run-time can be bounded as n gets larger by some constant times g(n)
       - For example, sum is O(n), i.e. linear

  • look at the lastElement and sumProduct method in AsymptoticsExamples code
       - What do they do?
       - What are their Big O running times?
          - Put another way, how does their run-time grow as you increase the size of the input?
       - lastElement
          - O(1), aka constant
          - no matter how large the array is (assuming .length is a constant time operation) it always does the same amount of work
       - sumProduct
          - O(n^2), aka quadratic
          - for each element, it must do a linear amount of work

  • if we know that sumProduct on an array of size 10,000 takes time t. Could you tell me approximately how long it would take on 20,000 numbers?
       - about 4t
          - We can figure this out by plugging our time into the O equation:
             - t time for size of size x array
                g(n) = n^2
                g(10000) = 10000^2 = t
             - doubling the size means:
                g(20000) = 20000^2 = (2 * 10000)^2 = 4 * 10000^2 = 4t
                
                - the time will roughly quadruple if we double the size
                
  • How does Big-O notation allow us to ignore irrelevant details?
       - look at the doubleSum method in AsymptoticsExamples code
          - What is the Big O runtime of this method?
             - O(n)
          - How does it's runtime compare to that of sum?
             - twice as long (calls sum twice)
       - Even though doubleSum is twice as slow as sum, they're still in the same category since they will roughly grow at the same rate

  • Show running time table https://cs.pomona.edu/classes/cs62/lectures/big_O.jpg

  • 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?