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
findMinfindMaxfind(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