Searching is a very common problem, both in life and in computer
applications.
It is so important that many data structures and algorithms have
been designed specifically to make searching as easy and efficient
as possible.
In this set of notes we will review the three techniques that we
discussed the first day of class for searching for a value in an
array.
Other sets of notes will present several different data structures
designed to support efficient searches.
Recall that there are two basic approaches to
searching for a given value v in an array:
sequential search and binary search.
Sequential search involves looking at each value in turn (i.e., start
with the value in array[0], then array[1], etc).
The algorithm quits and returns true if the current value
is v; it quits and returns false if it has looked at all of the values in
the array without finding v.
If the values are in sorted order, then the algorithm can sometimes
quit and return false without having to look at all of the values in the array;
in particular, if the current value is greater than v then there
is no point in looking at the rest of the values in the array (v is
definitely not there).
The worst-case time for a sequential search in an array of size
N is always O(N), even
when the array is sorted (though the average-case time should
be better for a sorted array than for a non-sorted array).
When the values are in sorted order, a better approach than sequential
search is to use binary search.
The algorithm for binary search starts by looking at the middle item x.
If x is the value v that we're searching for, the algorithm quits and
returns true.
Otherwise, it uses the relative ordering of x and v to eliminate half
of the array (if v is less than x, then it can't be to the
right of x in the array;
similarly, if it is greater than x, it can't be to the left of x).
Once half of the array has been eliminated, the algorithm starts again
by looking at the middle item in the remaining half.
It quits when it finds v or when the entire array has been eliminated.
The worst-case time for binary search in an array of size N
is proportional to log2 N (pronounced "log base 2 of N").
The log base 2 of N is the number of times N can be divided in half
before there is nothing left.
Similarly, the log base 3 of N would be the number of times N can be
divided in thirds before there is nothing left.
Using Big-O notation, log base anything of N is written O(log N), since the
difference between "log base 2 of N" and "log base some other number of N"
is just a constant factor.
As discussed above, binary search only works if the values in the array
are in sorted order.
Values can be arranged in sorted order only if, given two values
a and b, we can answer the question: Is a less
than b?
That question can be answered for all numeric values (e.g., int, float,
double);
we can compare two numeric variables x and y
using the "less-than" operator: if (x < y) ...
What about Java Objects?
Since Integers and Doubles represent numeric values, it makes
sense to be able to compare them;
similarly, there is a natural ordering on strings, so it makes
sense to be able to compare two String objects, too.
For Integers and Doubles, you could convert them to the corresponding
int / double values and use the less-than operator, but Java gives you
an easier option (one that also works for Strings): Integers, Doubles,
and Strings all implement the Comparable interface,
which means that they have a compareTo method, which allows
you to compare two Integers, two Doubles, or two Strings to see
if one is less than, equal to, or greater than the other.
The compareTo method returns a negative value to mean "less than",
it returns zero to mean "equal to", and it returns a positive value
to mean "greater than".
For example, if variables S1 and S2 are both Strings, then
S1.compareTo( S2 ) returns an int value that is:
When you define a new class you should think about whether it makes
sense to ask whether one instance of the class is less than another
instance.
If the answer is yes, you should define a compareTo method, and you
should make your class implement the Comparable
interface.
If you do that, it will be possible to pass instances of your class
to methods that require Comparable parameters (for example,
Java provides some sorting methods that require Comparable parameters).
To make your class implement the Comparable interface you must:
Introduction
Searching in an Array
The Comparable Interface
For example, we could define a Name class with two fields for the
first and last names.
It makes sense to consider one Name smaller than another if the
first one would come first in the phonebook (i.e., its last name
comes first in alphabetical order, or the last names are the same
and its first name comes first in alphabetical order).
Here's a (partial) definition of the Name class, including just the
fields and the compareTo method:
int compareTo( Object ob )
public class Name implements Comparable {
private String firstName, lastName;
public int compareTo(Object ob) {
if (lastName.compareTo(((Name)ob).lastName) < 0) return -1;
return firstName.compareTo( ((Name)ob).firstName );
}
}
Note that to implement the Comparable interface, you must use the
correct signature for the compareTo method; in particular,
the type of the parameter must be Object.
However, it is OK to assume (in the code) that the parameter
is actually a Name -- you can cast it to a Name.
If at runtime the compareTo method is called with something other
than a Name, then a ClassCastException will be thrown
(and it is the responsibility of whoever called your compareTo
method to deal with that).