last updated 2/28/07
| while ( cond ){ body }
|
private void RecWhile() { if ( cond ) { body RecWhile(); } } |
// traverse a singly linked list
temp = head;
while (temp != null){
System.out.println(temp.value);
temp = temp.getLink();
}
|
private void printAll(Node temp){
if (temp != null){
System.out.println(temp.value);
printAll( temp.getLink() );
}
}
public printAll(){
printAll(head);
} LList.printAll(); |
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"
When a function (recursive or not) is called (invoked or activated) an activation frame on the call stack is created to hold:
The call stack is usually the system stack-- a special area of memory is reserved for the stack and one or more CPU registers are dedicated to managing the stack.
System.out.println( fact(3)); //original call
int fact(int n){ //n==3
if (n==0) return 1;
return n * fact(n-1);
}
int fact(int n){ //n==2
if (n==0) return 1;
return n * fact(n-1);
}
int fact(int n){ //n==1
if (n==0) return 1;
return n * fact(n-1);
}
int fact(int n){ //n==0
if (n==0) return 1;
return n * fact(n-1);
}
int fact(int n){ //n==1
if (n==0) return 1;
return n * 1;
}
int fact(int n){ //n==2
if (n==0) return 1;
return n * 1;
}
int fact(int n){ //n==3
if (n==0) return 1;
return n * 2;
}
return 6;
A stack is used to hold activation records for each method. Further, each invocation of a method during recursion causes an activation record to be pushed on the system stack.
Consider, then the cost of recursion with respect to local variables.

When a method/function is called
While the method executes, the local variables and parameters are simply found by adding a constant associated with each variable/parameter to the Base Pointer.
When a method returns