lecture 2: recursive functions & binary search › ~cheewtan › lec2_recursive_binary.pdf ·...
TRANSCRIPT
CS3334 Data Structures Lecture 2: Recursive Functions &
Binary Search
Chee Wei Tan
What is Recursive Function?
• A recursive function is a function that calls itself (i.e., a recursive call).
• A recursive function has base case(s), i.e., no more recursive calls.
• Given an input size n, recursive calls reduce n progressively until n reaches a base case.
2 Recursive Functions & Binary Search
Example: Fast Exponentiation (1/2)
• Compute xn, given x and integer n ≥ 0 • Assume multiplication between 2 values requires 1 step • Straightforward implementation requires O(n) time
– x8 = x · x · x · x · x · x · x · x – 7 multiplications, O(n-1) = O(n)
• However, a recursive function can do it in O(logn) time – It can compute x8 in 3 multiplications. – (x) · (x) = x2
(x · x) · (x · x) = x4
(x · x · x · x) · (x · x · x · x) = x8
3 Recursive Functions & Binary Search
Example: Fast Exponentiation (2/2)
4
double power(double x, int n)
{
if (n==0) return 1; //base case if (n==1) return x; //base case if (n%2==0) //n is even
return power(x*x,n/2); //recursive call else //n is odd
return power(x*x,n/2)*x; //recursive call /* n/2 is integer division */
}
1
2
3
4
5
6
Recursive Functions & Binary Search
Tree of Recursive Calls
• Drawing the tree of recursive calls is a technique to trace the execution of a recursive function
• Try to trace the call power(x,4) which computes x4 in the example, the tree of recursive calls is drawn in the next few slides
5 Recursive Functions & Binary Search
Recursive Calls of power(x,4) (1/5)
• The root node represents the initial call, i.e., power(x, 4)
power(x,4) double power(double x, int n)
{
if (n==0) return 1; //base case
if (n==1) return x; //base case
if (n%2==0) //n is even
return power(x*x,n/2);
else //n is odd
return power(x*x,n/2)*x;
}
1
2
3
4
5
6
6 Recursive Functions & Binary Search
Recursive Calls of power(x,4) (2/5)
• Since 4 is even (i.e., n > 1), the function makes 1st recursive call power(x2, 4/2=2). So, we draw a node representing power(x2, 2) and link it as a child of power(x, 4).
power(x,4)
power(x2,2) 1
double power(double x, int n)
{
if (n==0) return 1; //base case
if (n==1) return x; //base case
if (n%2==0) //n is even
return power(x*x,n/2);
else //n is odd
return power(x*x,n/2)*x;
}
1
1
2
3
4
5
6
7 Recursive Functions & Binary Search
Recursive Calls of power(x,4) (3/5)
• Since 2 is even (i.e., n > 1), the function makes 2nd recursive call power(x2x2=x4, 2/2=1). So, we draw a node representing power(x4, 1), and link it as a child of power(x2, 2).
double power(double x, int n)
{
if (n==0) return 1; //base case
if (n==1) return x; //base case
if (n%2==0) //n is even
return power(x*x,n/2);
else //n is odd
return power(x*x,n/2)*x;
}
power(x,4)
power(x2,2)
power(x4,1)
1
1
2
2
1
2
3
4
5
6
8 Recursive Functions & Binary Search
Recursive Calls of power(x,4) (4/5)
• Since n=1 (i.e., the second base case), 2nd recursive call power(x4, 4) returns x4 (Line 4)
power(x,4)
power(x2,2)
power(x4,1)
return x4
9
double power(double x, int n)
{
if (n==0) return 1; //base case
if (n==1) return x; //base case
if (n%2==0) //n is even
return power(x*x,n/2);
else //n is odd
return power(x*x,n/2)*x;
}
1 2
1
2
3
4
5
6
1
2
Recursive Functions & Binary Search
Recursive Calls of power(x,4) (5/5)
• 1st recursive call power(x2, 4) returns x4 (Line 4), i.e., the initial power(x, 4) returns x4.
power(x,4)
power(x2,2)
power(x4,1)
return x4
return x4
10
double power(double x, int n)
{
if (n==0) return 1; //base case
if (n==1) return x; //base case
if (n%2==0) //n is even
return power(x*x,n/2);
else //n is odd
return power(x*x,n/2)*x;
}
1
1
2
3
4
5
6
1
2
2
return x4
Recursive Functions & Binary Search
Worst-Case Time Complexity Analysis (General Framework)
• Let T(n) be the worst case time complexity of the given recursive function
• Find a recurrence formula for T(n) • Solve the recurrence formula by unrolling the
recurrence formula a few times to see the general pattern
11 Recursive Functions & Binary Search
Worst-Case Time Complexity (1/2)
• Let T(n) be the worst case running time of the algorithm (where n is the problem size).
• Let c be the constant time for the local work (the running time of the local work performed in the call is at most some constant c).
• Since at most one recursive call is made, the recurrence formula is T(n) ≤ T(n/2) + c.
• Assume n=2k, where k is an integer.
12 Recursive Functions & Binary Search
Worst-Case Time Complexity (2/2)
• Therefore, T(n) ≤ T(1) + clog2n = O(logn) (since n=2k, k=log2n)
13
T(n) ≤ T(n/2) + c
T(n/2) ≤ T(n/4) + c
T(n/4) ≤ T(n/8) + c …
+) T(2) ≤ T(1) + c T(n) ≤ T(1) + kc
k eq
uatio
ns
Find a recurrence formula for T(n)
Solve the recurrence formula by unrolling the recurrence formula a few times to see the general pattern
Recursive Functions & Binary Search
Worst-Case Space Complexity (1/3)
• For each recursive call, a separate copy of the variables declared in the function is created and stored in a stack. The storage is released when the recursive call finishes (e.g., a return statement is reached)
• Thus, the amount of space needed to implement a recursive function depends on the depth of recursion, not on the number of times the function is invoked.
14 Recursive Functions & Binary Search
Worst-Case Space Complexity (2/3)
• Let S(n) be the worst case space requirement of the algorithm (where n is the problem size).
• Let c be the constant space for the local storage (the local storage required in the call is at most some constant c).
• Since at most one recursive call is made, the recurrence formula is S(n) ≤ S(n/2) + c.
• Assume n=2k, where k is an integer.
15 Recursive Functions & Binary Search
Worst-Case Space Complexity (3/3)
• Therefore, S(n) ≤ S(1) + clog2n = O(logn) (since n=2k, k=log2n)
16
S(n) ≤ S(n/2) + c
S(n/2) ≤ S(n/4) + c
S(n/4) ≤ S(n/8) + c …
+) S(2) ≤ S(1) + c S(n) ≤ S(1) + kc
k eq
uatio
ns
Find a recurrence formula for S(n)
Solve the recurrence formula by unrolling the recurrence formula a few times to see the general pattern
Recursive Functions & Binary Search
Worst-Case Time and Space Complexity
• The tree of recursive calls can visualize the time and space complexity
• Time complexity is proportional to the number of nodes in the tree.
• Space complexity is proportional to the length of longest root-to-leaf path.
17 Recursive Functions & Binary Search
Recursive Binary Search
• Elements in A[] are sorted in increasing order.
18
int binarySearch(int A[], int low, int up, int x)
{
if (low>up) return -1; //cannot find x
int mid = (low+up)/2;
if (A[mid]==x) return mid; //find x
else if (A[mid]<x)
return binarySearch(A,mid+1,up,x);
else
return binarySearch(A,low,mid-1,x);
}
Recursive Functions & Binary Search
binarySearch(A, 0, 5, 28)
19
15 28 30 32 35 48
Sorted Array A
x = 28, low = 0, up = 5
low up
(Index) 0 1 2 3 4 5
Recursive Functions & Binary Search
binarySearch(A, 0, 5, 28)
20
15 28 30 32 35 48
Sorted Array A
x = 28, low = 0, up = 5 1st recursion: mid = (0 + 5) / 2 = 2 Since 28 < A[2], up = mid - 1 = 2 - 1 = 1
low up mid
(Index)
int binarySearch(int A[], int low, int up, int x)
{
if (low>up) return -1;
int mid = (low+up)/2;
if (A[mid]==x) return mid;
else if (A[mid]<x)
return binarySearch(A,mid+1,up,x);
else
return binarySearch(A,low,mid-1,x); }
0 1 2 3 4 5
Recursive Functions & Binary Search
binarySearch(A, 0, 1, 28)
21
15 28 30 32 35 48
Sorted Array A
x = 28, low = 0, up = 1 2nd recursion: mid = (0 + 1) / 2 = 0 Since 28 > A[0], low = mid + 1 = 0 + 1 = 1
low, mid up
(Index)
int binarySearch(int A[], int low, int up, int x)
{
if (low>up) return -1; //cannot find x
int mid = (low+up)/2;
if (A[mid]==x) return mid; //find x
else if (A[mid]<x)
return binarySearch(A,mid+1,up,x); else
return binarySearch(A,low,mid-1,x);
}
0 1 2 3 4 5
Recursive Functions & Binary Search
binarySearch(A, 1, 1, 28)
22
15 28 30 32 35 48
Sorted Array A
x = 28, low = 1, up = 1 3rd recursion: mid= (1 + 1) / 2 = 1 Since 28 = A[1], return 1
low, up, mid
(Index)
int binarySearch(int A[], int low, int up, int x)
{
if (low>up) return -1; //cannot find x
int mid = (low+up)/2;
if (A[mid]==x) return mid; //find x else if (A[mid]<x)
return binarySearch(A,mid+1,up,x);
else
return binarySearch(A,low,mid-1,x);
}
0 1 2 3 4 5
Recursive Functions & Binary Search
Worst-Case Time Complexity
• Let T(n) be the worst case running time of the algorithm (where n is the problem size).
• Let c be the constant time for the local work (the local work performed in the call is at most some constant c).
• Since at most one recursive call is made, the recurrence formula is T(n) ≤ T(n/2) + c.
• Assume n=2k, where k is an integer.
23 Recursive Functions & Binary Search
Worst-Case Space Complexity
• Let S(n) be the worst case space requirement of the algorithm (where n is the problem size).
• Let c be the constant time for the local storage (the local storage required in the call is at most some constant c).
• Since at most one recursive call is made, the recurrence formula is S(n) ≤ S(n/2) + c.
• Assume n=2k, where k is an integer.
24 Recursive Functions & Binary Search
Fibonacci Sequence
• The Fibonacci sequence is defined as: f0 = 0, f1 = 1 fi = fi-1 + fi-2 for i ≥ 2
25
// to compute fn int fib(int n)
{
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2);
}
Recursive Functions & Binary Search
Tracing fib(4)
• To trace the execution for the call fib(4), the tree of recursive calls (next slide) is drawn as follows: – The root node represents the initial call, i.e., fib(4) – The call fib(4) makes two recursive calls, fib(3) and
fib(2). So, we draw two nodes representing fib(3) and fib(2), and link them as children of fib(4).
– We repeat this until all the recursive calls are drawn – Then, we determine the return value of each call from
bottom to top
26 Recursive Functions & Binary Search
Tree of Recursive Calls for fib(4)
27
4
1 0
3
2 1
2
1 0
1 0
1 1
2
1 0
1
3
Return values are shown in red italic
Recursive Functions & Binary Search
28
4
int fib(int n){ if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2);
}
Storage for recursive calls
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
29
4
3
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R1
R1
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
30
4
3
2
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R2
R1
R1
R2
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
31
4
1
3
2
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R3
R2
R1
R1
R2
R3
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
32
4
1
3
2
1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1) return n; return fib(n-1)+fib(n-2);
}
Storage for recursive calls
R2
R1
R1
R2
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
33
4
1 0
3
2
1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R3
R2
R1
R1
R2
R3
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
34
4
1 0
3
2
1 0
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1) return n; return fib(n-1)+fib(n-2);
}
Storage for recursive calls
R2
R1
R1
R2
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
35
4
1 0
3
2
1 0
1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R1
R1
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
36
4
1 0
3
2 1
1 0
1 1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R2
R1
R1
R2
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
37
4
1 0
3
2 1
1 0
1 1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1) return n; return fib(n-1)+fib(n-2);
}
Storage for recursive calls
R1
R1
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
38
4
1 0
3
2 1
1 0
1 1
2
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
39
4
1 0
3
2 1
2
1 0
1 1
2
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R1
R1
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
40
4
1 0
3
2 1
2
1
1 0
1 1
2
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R2
R1
R1
R2
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
41
4
1 0
3
2 1
2
1
1 0
1 1
2
1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1) return n; return fib(n-1)+fib(n-2);
}
Storage for recursive calls
R1
R1
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
42
4
1 0
3
2 1
2
1 0
1 0
1 1
2
1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
R2
R1
R1
R2
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
43
4
1 0
3
2 1
2
1 0
1 0
1 1
2
1 0
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1) return n; return fib(n-1)+fib(n-2);
}
Storage for recursive calls
R1
R1
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
44
4
1 0
3
2 1
2
1 0
1 0
1 1
2
1 0
1
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
45
4
1 0
3
2 1
2
1 0
1 0
1 1
2
1 0
1
3
Return values are shown in red italic
int fib(int n){
if (n==0 || n==1)
return n;
return fib(n-1)+fib(n-2); }
Storage for recursive calls
Tree of Recursive Calls for fib(4)
Recursive Functions & Binary Search
Note: Increasing Functions
• A function f(n) is called increasing, if for all x and y such that x ≤ y, f(x) ≤ f(y), so f(n) preserves the order.
46
f(n)
n
Recursive Functions & Binary Search
Worst-Case Space Complexity (1/2)
• Let S(n) be the worst-case space required by fib(n). • Let c be the constant space for the local storage (the local
storage required in the call is at most some constant c). • The calls fib(n-1) and fib(n-2) execute one after the
other; hence, storage can be reused. • Therefore, we can set up the following recurrence
formula for S(n): S(n) = max{ S(n-1), S(n-2) } + c
• Assuming S(n) is an increasing function (i.e., S(n-1) ≥ S(n-2)), S(n) = S(n-1) + c
47 Recursive Functions & Binary Search
Worst-Case Space Complexity (2/2)
• Solving the recurrence formula: S(n) = S(n-1) + c S(n-1) = S(n-2) + c S(n-2) = S(n-3) + c … +) S(2) = S(1) + c S(n) = S(1) + (n-1)c
= O(n)
48 Recursive Functions & Binary Search
The Beauty of Data Structures
Introduction 49
What is its Tree of Recursion?
What is its Tree of Recursion?