Thought experiment: searching ordered vs unordered sets
Assume:
To search for a value x
Look at middle index i
of array
if arr[i]
is smaller than x
, search right half of array; otherwise search left half
recursively search the half of array determined in step 2
x
to midpoint of sub-intervalx
and value foundfind(51)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
arr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
arr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
arr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
arr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
A Single Step:
Input:
x
element to be foundarr
a sorted array storing elementsi
, j
indices with i < j
x
in range arr[i], arr[i+1], ..., arr[j-1]
Next Step:
i = j - 1
, determine if x
is at arr[i]
i', j'
to search// search arr for value x between indices i and j
binarySearch(int[] arr, int x, int i, int j) {
if (j == i + 1) {return arr[i];}
int k = (i + j) / 2;
if (arr[k] <= x) {
return binarySearch(arr, x, k, j);
} else {
return binarySearch(arr, x, i, k);
}
}
binarySearch(int[] arr, int x, int i, int j) {
if (j == i + 1) {return arr[i];}
int k = (i + j) / 2;
if (arr[k] <= x) {return binarySearch(arr, x, k, j);
} else { return binarySearch(arr, x, i, j);}
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
arr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53]
find(51)
-> binarySearch(arr, 51, 0, 16)
initial call to binarySearch(arr, x, 0, n)
has $j - i = n$
next recursive call is binarySearch(arr, x, i, j)
with $i - j = n / 2$
each subsequent recursive call cuts size of region in half
in $k$th call, $i - j = n / 2^k$
value returned when $i - j = 1$
$k$ satisfying $n / 2^k = 1$
$\implies 2^k = n$
$\implies k = \log n$
Each recursive call takes $O(1)$ time, so overall running time is $O(\log n)$
Observe:
SimpleUSet
add
, remove
, find
findMin
findMax
find(x)
returns the smallest element y
in the set that is no smaller than x
(null
if no such y
)Comparable
InterfaceTo indicate that elements of class E
can be compared, E
must implement the Comparable<E>
interface:
public interface Comparable<T> {
int compareTo(T o);
}
This interface is built in to Java!
Interpretation:
x.compareTo(y) < 0
indicates that x
is “smaller than” y
x.compareTo(y) > 0
indicates that x
is “larger than” y
x.compareTo(y) == 0
indicates that x
is semantically equivalent to y
x.compareTo(y) == 0
if and only if x.equals(y)
Integer
public class Integer implements Comparable<Integer> {
private int value;
@Override
int compareTo(Integer x) {
return value - x.value;
}
@Override
boolean equals(Object o) {
if (!(o instanceOf Integer)) return false;
Integer x = (Integer) o;
return (value == x.value);
}
}
public interface SimpleSSet<E extends Comparable<E>> extends SimpleUSet<E> {
@Override /* comments explain how find differs from parent method */
E find(E x);
E findMin();
E findMax();
}
SimpleSSet
Question. What data structure should we use to store elements?
… add(x)
?
… remove(x)
?
… find(x)
?
Find the index where x
would be located
Define int getIndex(x)
method
How to find(x)
?
How to add(x)
?
Once getIndex
is implemented add
, remove
, find
can be made to work
getIndex
will not affect add
/remove
/find
codeSuggestion. First implement/test with simple getIndex
method, then design/test more sophisticated getIndex
implementations
Maxim. Premature optimization is the root of all evil.
ArraySimpleSSet
ImplementationSee code!
getIndex
firstCompare running times of find
between unordered set implementation (ArraySimpleUSet
) and ArraySimpleSSet
with binary search