last updated 3/13/04
Linear searching of an array:
Iterative: start at one end, scanning the data for the value, and if found, stop and return the position.
Recursive: check first element for key, return position if found, otherwise search the remaining elements of the array
private int linearSearch( Object key, int pos){ if(pos >= vec.length) { return -1; } else if (vec[pos].equals(key)) { return pos; } else { return linearSearch(key, pos+1); } }The invocation would be pos = myArray.linearSearch( value, 0); which introduces an additional parameter that is superfluous for the user. So we define a public helper function:
public int linearSearch(Object key){ return linearSearch(key, 0); //start at 0 }
Binary searching of an array:
Iterative: start at middle, if value found, stop and return the position, else determine whether to search top half or bottom half, tracking the boundaries of the search area
Recursive: if segment is empty, return -1; if middle of segment == key return position, otherwise search top half or bottom half
private int binarySearch(Object key, int lo, int hi){ if(lo > hi) { return -1; } else { int mid = (lo+hi)/2; int comp = vec[mid].compareTo(key); if(comp == 0){ return mid; } else if(comp < 0){ return binarySearch(key, mid+1, hi); } else { return binarySearch(key, lo, mid-1); } }The invocation would be pos = myArray.binarySearch( value, 0, myArray.size()-1); which introduces an additional parameter that is superfluous for the user. So define a helper function:
public int binarySearch(Object key){ return binarySearch(key, 0, size-1); }
The formal approach to determining the big-O complexity of an algorithm is to set up recurrence relations and solve them.
For the recursive linear search we have
T(n) = T(n-1) + 1
T(0) = 1That is, the time for searching an array of size n is the time to search an array of size n-1 plus the cost of searching (checking or comparing) the first element which is constant. The cost for searching an empty array is constant as well.
Solving:
T(n-1) = T(n-2) + 1,
so T(n) = T(n-1)+1 = [T(n-2) + 1] + 1 = T(n-2) + 2,
and T(n-2) = T(n-3) +1
so T(n) = T(n-2) + 2 = [T(n-3) + 1] +1 = T(n-3) + 3,and in general T(n) = T(n-i) + i;
so finally T(n) = T(0) + n = 1 + n = O(n)
For the recursive binary search we have
T(n) = 1 + T(n/2)
T(1) = 1"The time to search n elements is a constant computation plus the time it takes to search n/2 elements."
Solving (rather intuitively) and assuming n is an even power of two (if not, we add dummy elements to the array to make it a size that is the next even power of two)
T(n/2) = 1 + T(n/4),
and T(n/4) = 1 + T(n/8), etc.So T(n) = 1 + T(n/2) = 1 + 1 + T(n/4) = 1 + 1 + 1 + T(n/8)
and the pattern is T(n) = k + T(n/2k)
now when k = log n, then n/(2log n)=n/n = 1,
so T(n) = log n + 1 = O(log n)
When you have simple recurrence relations of the form
Recursive factorial computation is of what complexity?
Consider the Fibonacci sequence recursive calculation:
F(1) = 1
F(2) = 1
F(n) = F(n-1)+F(n-2)
Its recurrence relation time is
T(1) = 1
T(2) = 1
T(n) = T(n-1) + T(n-2) + 1Solving is a little beyond this course, but T(n) = O(2n) or "exponential"