You are on page 1of 63

CHAPTER 2

ALGORITHMS
AND
RECURSIONS
Data Structures Featuring C++ © Sridhar Radhakrishnan 1
Algorithms
• Algorithm consists of a set of finite steps satisfying the following conditions:

 Input: Number and type of input values must be made clear.


 Precise specification of each step: Each step or instruction must be feasible
and unambiguously defined.
 Finiteness: For all input possibilities, the algorithm must terminate in finite time.
 Result: The objective of the algorithm must be made clear.
There may be an output that spells out the execution of the algorithm.

• Programs involving graphical user interfaces do not necessarily terminate in finite


time but k eep w aiting for user input (usually a “click” on an icon).

• Such programs called procedures fall under the general category of reactive systems.
Eg.: A computer operating system

Data Structures Featuring C++ © Sridhar Radhakrishnan 2


Resources and Complexities

• Resources generally attributed to a computer algorithm are space and running time.

• Space complexity : Amount of memory required.


• Time complexity : Amount of time to complete execution.

• Running tim e and space required are ex pressed as a function of input size n.

• Example: Algorithm which reads n numbers and prints them

• Tim e com plex ity = n


• Space com plex ity = 1

Data Structures Featuring C++ © Sridhar Radhakrishnan 3


Study of Effectiveness of an Algorithm
• An approach to evaluate the effectiveness of an algorithm is to perform empirical
studies.

• Limitations of Empirical studies:


 Algorithm has to be implemented and comparison of two algorithms implies that
both of them have to implemented for the same machine using the same language.

 It is impossible to test for all possible input permutations of data.

• General approach for Performance Analysis:


 Comparison of different algorithms for the same problem has to be done in the
same computer environments.

Data Structures Featuring C++ © Sridhar Radhakrishnan 4


Algorithm Expressed in pseudo-Language

• Example: Pseudo-language description for sorting an array A of n integers in


ascending order.

• Let us use the notation A[i:n] to denote the set of array elements A[i],A[i+1],...,A[n].

• We first find the minimum integer in the array A[1:n] and swap it with the number in
A[1].

• Then we find the minimum in the array A[2:n] and swap it with the number in A[2] and
so on.

Data Structures Featuring C++ © Sridhar Radhakrishnan 5


Simple Sort Algorithm

Algorithm Simplesort(A, n)
Input : An array A of n integers.
Output : A sorted array A in ascending order of the numbers it holds.

FOR i←1 to (n-1) do


Find the minimum integer in the array A[i:n] :
Let j be such that A[j]=min A[i:n]
Swap A[i] with A[j]
ENDFOR

Data Structures Featuring C++ © Sridhar Radhakrishnan 6


PERFORMANCE OF ALGORITHMS

Data Structures Featuring C++ © Sridhar Radhakrishnan 7


Measuring Program Execution Time
• Measuring execution time of a program takes the following steps:
 Start a Timer
 Run the Program
 End the Timer

• Wall-clock time is inaccurate and not precise (measures only up to seconds precision)

• Embed statements within the program


 Start = clock();
 // code for program that is being measured
 End = clock();
 CPUTimeUsedByProgram = (end-start)

• When a program is preempted, the measure clock will be include time taken by the
CPU to do other things.

Data Structures Featuring C++ © Sridhar Radhakrishnan 8


Analysis of Algorithm
• Given an algorithm a mathematical estimate is determined that reflects the time and
space complexity of an algorithm.

• This estimate should closely reflect the experimental results to the extent possible.

• Any code or pseudo-language describing an algorithm consists of certain number of


primitive operations or instructions.

• Primitive operations:
 Assignm ent of a value to a variable.
 Com paring tw o num bers.
 Arithm etic operations such has addition, subtraction, m ultiplication etc.
betw een tw o num bers.

Data Structures Featuring C++ © Sridhar Radhakrishnan 9


Analysis of Algorithm (cont.)
Primitive operations contd…

 I nvok ing a function or a m ethod and returning from a function or


m ethod.
 I ndex ing into an array.
 Unconditional jum ps from one step of the code to another.

• The execution times of these operations depend on the architecture of the machine
that implements the algorithms.

Data Structures Featuring C++ © Sridhar Radhakrishnan 10


Analysis of Algorithm SUM
• Example: Sum of n integers in an array A.

Algorithm SUM(A,n)
Input : An array A storing n integers.
Output : Sum of the integers in the array, i.e., A[1]+A[2]+...+A[n].
1. sum←0 // 1 primitive operation
2. i ← 1 // 1 primitive operation
3. IF i>n THEN GO TO step 7 // 1*(n+1) + 1 primitive operations
4. sum ← sum+A[i] // 3*n primitive operations
5. i ← i+1 // 2*n primitive operations
6. GO TO step 3 // 1*n primitive operations
7. RETURN sum // 1 primitive operation
• Total number of operations = 7n+2+3.
• We say that the running time is proportional to 7n+5.
• The running time of the above algorithm is independent of the values
that are stored in the array A.

Data Structures Featuring C++ © Sridhar Radhakrishnan 11


Analysis of Algorithm FINDMIN
Example: Find the minimum value in an array A of n integers.

Algorithm FINDMIN(A,n)
Input : An array A storing n integers.
Output : The minimum value minA among all numbers in A.
1. minA←A[1] // 2 primitive operations
2. i ← 2 // 1 primitive operation
3. IF i>n THEN GO TO step 7 // 1*(n) + 1 primitive operations
4. IF A[i]< minA THEN minAA[i] // ??? primitive operations
At least 2*(n-1)
At most 2*(n-1) + 2*(n-1)
5. i ← i+1 // 2*(n-1) primitive operations
6. GO TO step 3 // 1*(n-1) primitive operations
7. RETURN minA // 1 primitive operation

Best case = 6n-1


Worst case = 8n-3

Data Structures Featuring C++ © Sridhar Radhakrishnan 12


PERFORMANCE OF ALGORITHMS -
CONTINUED

Data Structures Featuring C++ © Sridhar Radhakrishnan 13


Sample Complexities
for (i=0; i < n; i++) //5n+3
{
x = x + 1; //n*2
}

i = n;
while (i >= 1) {
i ← i/2
}

for (i=0; i < n; i++) //5n+3


{
for (j=0; j < n; j++) x = x + 1;//n*(7n+3)
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 14


for (i=0; i < n; i++) //3n+1
{
j = 0; //n*1
while (j < n) //n*(n+1)
{
x = x + 1; //n*n*2
j = j + 1;//n*n*2
}
}

5n2 + 8n + 4

Data Structures Featuring C++ © Sridhar Radhakrishnan 15


Analysis of Algorithm
• Algorithms can be analyzed in three different ways:

 Best case inputs


 W orst case inputs
 Average case inputs

• If the nature of the input is unknown,then worst case estimate is a better tool to
compare the performance of the algorithms than the best case estimate.

• Analysis can be simplified by lumping a set of primitive operations.

• Example: a = b*2 + A[i]*B[i] can be considered as one lum ped up operation.

Data Structures Featuring C++ © Sridhar Radhakrishnan 16


Asymptotic Notation and Big-Oh Notation
• Let f(n) and g(n) be functions mapping nonnegative integers to real numbers.

• We say that f(n) ∈ O(g(n)) (read as “f(n) is of order g(n)” or as “f(n) is big-oh of g(n)”)
if there is a real number c>0 and a fixed integer n0>=1 such that
f(n)<=cg(n) for every integer n>=n 0 .
f(n)=c 1 n+c 2 = O(n) .

• To prove this we can take c=2c 1 and n 0 =c 2 / c 1 .


• Then we can verify that c 1 n+c 2 <= 2c 1 n for all n>=c 2 / c 1 .

• The big-oh notation is also somewhat loose in the following sense.


• Given a function f(n)= n, it is true that f(n)∈ O(n), f(n)∈ O(n log n), f(n)∈ O(n2), and
f(n)∈ O(2n).

Data Structures Featuring C++ © Sridhar Radhakrishnan 17


Asymptotic Notation and Big-Oh Notation (cont.)

• For instance,if we had an algorithm with step-count


c1n2+c2n+c3,

• We should avoid saying that the running time is O(n 3 ) , although it is technically
correct to say it.

• The “big-Om ega ” notation captures the notion “greater than or equal to”.

• Formally, we say that a function f(n) ∈ Ω(g(n)) if there is a constant c>0 and an
integer n0>=1 such that f(n)>=cg(n), for all n>=n0.

• In this case, g(n) is a lower bound of f(n).

Data Structures Featuring C++ © Sridhar Radhakrishnan 18


Asymptotic Notation and Big-Oh Notation (cont.)
• Analogously, the “big-Theta” function captures the notion of “exact order”.

• Formally, f(n) ∈ Θ(g(n)) if f(n) ∈ O(g(n)) and f(n) ∈ Ω(g(n)).

• For example, f(n)=log n+n ∈ Ω(n) and g(n)=n2+n+log n ∈ Θ(n2).

• The lower bound defines the best possible efficiency of any algorithm that solves the
problem, including any algorithms that may be discovered in the future.

• The low er bound of the sorting algorithm is O(nlogn), where n is the number of
numbers to be sorted.

Data Structures Featuring C++ © Sridhar Radhakrishnan 19


Time Required for Various Time
Complexities
Input size log n n nlogn n2 n3 2n

10 3.322 ns 10 ns 33 ns 100 ns 1 μs 1 μs
20 4.322 ns 20 ns 86 ns 400 ns 8 μs 1 ms
30 4.907 ns 30 ns 147 ns 900 ns 27 μs 1 sec
40 5.322 ns 40 ns 213 ns 2 μs 64 μs 18.3 min

50 5.644 ns 50 ns 282 ns 3 μs 125 μs 13.0


days
100 6.644 ns 100 ns 664 ns 10 μs 1 ms 40,197
aeons
1000 10 ns 1 μs 10 μs 1 ms 1 sec
10,000 13 ns 10 μs 133 μs 100 ms 16.7 min
100,000 17 ns 100 μs 2 ms 10 sec 11.6 days

1,000,000 20 ns 1 ms 20 ms 16.7 min 31.7 years

Data Structures Featuring C++ © Sridhar Radhakrishnan 20


Plot of Various Algorithmic Complexities
Comparison of Compexities

10000000000

1000000000

100000000

10000000

1000000
Log n
Time Complexity

n
n Log n
100000
n^2
n^3
2^n
10000

1000

100

10

1
2 4 8 16 32 64 128 256 512 1,024
Input Size

Data Structures Featuring C++ © Sridhar Radhakrishnan 21


RECURSION

Data Structures Featuring C++ © Sridhar Radhakrishnan 22


Recursive Algorithms
• Direct Recursion : A function may call itself before it finishes execution.
• Indirect Recursion: Two functions calling each other.

• For recursion to be successful the recursive call must be on a problem smaller than the
original.

• Any recursive program consists of two parts: base case and recursive part.

• Example: Factorial of n numbers.


Algorithm Factorial(n )
Input : A nonnegative integer n.
Output : The factorial of n denoted by n! = n(n-1)(n-2)...1
1. IF (n=0 OR n=1) THEN RETURN 1
2. RETURN n × Factorial(n-1)

• The time complexity of the above algorithm is O(n).

Data Structures Featuring C++ © Sridhar Radhakrishnan 23


Fibonacci Numbers
• Fibonacci sequence f n :
f0=0, f1=1
fn=fn-1 + fn-2 for n >1

• First several numbers of the sequence:


0,1,1,2,3,5,8,13,21,34,55,89………

• Natural recursive algorithm REC_FIBONACCI:

ALGORITHM REC_FIBONACCI(N)
Input :The positive integer value N
Output:The Nth Fibonacci number
1. IF N = 0 THEN RETURN 0
2. IF N<=2 THEN RETURN 1
3. ELSE RETURN REC_FIBONACCI(N-1) + REC_FIBONACCI(N-2)

Data Structures Featuring C++ © Sridhar Radhakrishnan 24


Recursive Calls Generated for REC_FIBONACCI(5)

Fib(5)
return Fib(4) + Fib(3)

Fib(4) Fib(3)
return Fib(3) + Fib(2) return Fib(2) + Fib(1)

Fib(3) Fib(2) Fib(2) Fib(1)


return Fib(2) + Fib(1) return 1 return 1 return 1

Fib(2) Fib(1)
return 1 return 1

Data Structures Featuring C++ © Sridhar Radhakrishnan 25


Exponentiation
• The Exponent of an integer can be computed using either of the following methods.
 Ex ponent(N , x ) = N x for any integer N>0
 Ex ponent(N, x ) = N * N * N * … * N for x number of times

• Example: Ex ponent(2,5) = 2 5 = 2 * 2 * 2 * 2 * 2 = 32 .

• The first step in recursively defining Exponent(N) is to define


Exponent(N) in terms of the exponent of a smaller number

• This can be done as


 Ex ponent(N , x ) = N * Ex ponent(N, x -1)
 Base case is Ex ponent(N , 0) = 1 .

Data Structures Featuring C++ © Sridhar Radhakrishnan 26


Exponentiation (cont.)
• Complete definition of recursive exponent function is :

Exponent(N, x) = { 1
N*Exponent(N, x-1)
if x = 0.
if x > 0.

• Thus, the algorithm for calculating the exponent is:

Algorithm EXPONENT(N,x )
Input : The positive integer value N and x.
Output:: The factorial of N

1. IF x=0 THEN RETURN 1


2. ELSE RETURN N*EXPONENT(N,x-1)

Data Structures Featuring C++ © Sridhar Radhakrishnan 27


Writing a String Backwards
• Given a string of characters, writing it backwards would be to print it in the reverse
order of the occurrence of the characters that make up the string.

• Example : String “ cat ” should be written as “ tac ” .

• The first step in recursively defining the function WriteBackward(S) is to define


WriteBackward(S) in terms of the same function on a smaller string S.

• Writing string S backward is equivalent to writing the last character and then writing
the string S decreased by the last character in reverse.

WriteBackward(S) = output the last character and


WriteBackward(S - last char)

Data Structures Featuring C++ © Sridhar Radhakrishnan 28


Writing a String Backwards (cont.)
• The base case of the WriteBackward algorithm is when the string has no
more characters and in this case WriteBackwards will simply not do
anything and return to the previous call.

• The complete recursive definition of the WriteBackwards function is:

WriteBackward(S) =
{ do nothing if length of S is 0.
Output last character + WriteBackwards(S-1) if length of S > 0.

Algorithm WriteBackward(S)
Input : The string S with length S.length.
Output: The reverse of the string S.

1. IF S.length > 0 THEN


2. OUTPUT (S.charAT (S.length))
3. WriteBackward(S.subString(0,S.length-1))
4. ENDIF

Data Structures Featuring C++ © Sridhar Radhakrishnan 29


Writing a String Backwards (cont.)

• The function subString (beginI ndex , endI ndex ) is a built in function,


which will create a substring with the specified indexes.

• Also, charAt(index ) function returns the character at the specified index.

• The S.length-1 is needed to specify the last index because the string is indexed
from zero to (length-1).

Data Structures Featuring C++ © Sridhar Radhakrishnan 30


Execution of WriteBackwards(S)
• Example: Consider the string “h e l l o”

• The recursive calls placed on the stack and the corresponding output are as follows:

WriteBackwards(“hello”) = output “o” then call WriteBackwards(“h e l l”)


WriteBackwards(“hell”) = output “l” then call WriteBackwards(“h e l”)
WriteBackwards(“hel”) = output “l” then call WriteBackwards(“h e” )
WriteBackwards(“he”) = output “e” then call WriteBackwards(“h”)
WriteBackwards(“h”) = output “h” then call WriteBackwards(“”)

• At this stage the WriteBackwards (“”) statement is the base case and will not
display anything and the program terminates.

Data Structures Featuring C++ © Sridhar Radhakrishnan 31


Towers of Hanoi
• There are arbitrary number of N disks with three poles.
Pole A(source),Pole B(spare) and Pole C(destination).

Initial State: Final State:

A B C A B C
This problem has a recursive solution which can be described in three steps.

1. Ignore the largest disk at the bottom and move the top N-1 disks from A to B (using C as a spare hole)
2. Move the largest disk from A to C.
3. Now move the N-1 disks from B to C using A as spare.

Data Structures Featuring C++ © Sridhar Radhakrishnan 32


Algorithm for Towers of Hanoi Problem

ALGORITHM SOLVETOWERS(count,source,destination,spare)
Input : The number of disks (count) and three pegs.
Output: Move all disks from the source peg to destination peg following
the rules and using the spare peg.

1. IF(count=1) THEN
2. Move a disk directly from source to destination.
3. ELSE
4. SolveTowers(count-1,source,destination ,spare)
5. SolveTowers(1,source,destination ,spare)
6. SolveTowers(count-1,source,destination ,spare)
7. ENDIF

Data Structures Featuring C++ © Sridhar Radhakrishnan 33


Eight Queens Problem
• This problem involves a chessboard containing 64 squares that form 8 rows
and 8 columns.

• The most powerful piece in a chess game is the queen since it can attack any other
piece within its row, within its column, or along its diagonal.

• The eight queens problem is to figure out how to place eight queens on the chessboard
so that no queen can attack any other queen.

• No queen can reside in a row or column that contains another queen simplifies the
problem.

• Now the problem is only to check for possible attacks along the diagonals.

Data Structures Featuring C++ © Sridhar Radhakrishnan 34


Eight Queens Problem (cont.)
• This is has an elegant recursive solution which also involves some back tracking.

• The algorithm should place a queen in a column, provided that the queens have been
places correctly on the previous columns.

• If there are no more columns to consider, this is the base case.

• Otherwise, once a queen is placed correctly on the current column the next column
needs to be considered.

• The problem starts with 8 columns and recursively breaks down to a problem with
fewer columns.

Data Structures Featuring C++ © Sridhar Radhakrishnan 35


Eight Queens Problem (cont.)

• Complication in the problem:


 Sometimes a queen cannot be placed correctly on a particular column.

 Here the algorithm needs to backtrack to the previous column and place the previous queen
differently.

• Example of such a complication:


 If considering column 6 and the queen may not be placed on any of column 6’s rows, the
algorithm will backtrack to column 5 and change the position of column 5’s queen.

Data Structures Featuring C++ © Sridhar Radhakrishnan 36


Algorithm for the Eight Queens Problem
Algorithm PlaceQueens (Column)
Input: Column is an integer that is initially 1 where the queen will be placed.
Output: Place the Queens in such way that they do not attack each other.

1. IF (Column > 8) THEN


2. The problem is solved
3. ELSE
4. WHILE ( unconsidered squares exist in Column and the problem is unsolved)
5. Determine the next square in the Column that is not under attack by a queen in
the earlier column
6. IF (such a square exists) THEN
7. Place queen in that square
8. PlaceQueens (Column + 1) // try next column
9. IF (queen cannot be placed in Column + 1) THEN
10. Remove queen from Column and consider the next square in that column
11. ENDIF
12. ENDIF
13. ENDWHILE
14. ENDIF 37
Recursive Solution to Eight Queens Puzzle

Q
Q

Q
Q

Data Structures Featuring C++ © Sridhar Radhakrishnan 38


IMPLEMENTING RECURSION

Data Structures Featuring C++ © Sridhar Radhakrishnan 39


Use of stack in Implementation of Recursive Algorithms
• Memory organization for a typical executable code.

Data Structures Featuring C++ © Sridhar Radhakrishnan 40


Use of stack in Implementation of Recursive Algorithms (cont.)
• Example: Recursive implementation of the factorial function.
• This example illustrates the use of stack which plays a crucial role in the
implementation of recursive algorithms.

#include<iostream>
using namespace std;
int factorial(int n);
{
int f;
if (n<1) return 1; Initially
f=factorial(n-1); Stack contents
// fact() return address
return n*f; Stack ptr
}
int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
return 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 41


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
int factorial(int n);
Parameter
{
Stack contents
int f;
n 4
if (n<1) return 1;
f=factorial(n-1); stack ptr

// fact() return address


return n*f;
}
int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 42


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
int factorial(int n);
{ Return address
int f; Stack contents

if (n<1) return 1; n 4
f=factorial(n-1); main() rtn adrs
// fact() return address stack ptr
return n*f;
}
int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 43


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
int factorial(int n);
{ Local variable
int f; Stack contents
if (n<1) return 1; n 4
f=factorial(n-1);
main() rtn adrs
// fact() return address
f ???
return n*f;
stack ptr
}
int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 44


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
int factorial(int n);
{ Parameter
int f; Stack contents
if (n<1) return 1; n 4
f=factorial(n-1);
main() rtn adrs
// fact() return address
f ???
return n*f;
n 3
}
stack ptr
int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 45


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
int factorial(int n);
{ Return address
int f; Stack contents
if (n<1) return 1; n 4
f=factorial(n-1);
main() rtn adrs
// fact() return address
f ???
return n*f;
n 3
}
fact() rtn adrs
int main()
{ stack ptr

cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 46


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
int factorial(int n);
{ Local variable
int f; Stack contents
if (n<1) return 1; n 4
f=factorial(n-1); main() rtn adrs
// fact() return address ???
return n*f; n 3
} fact() rtn adrs
int main()
f ???
{
stack ptr
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 47


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
Parameter
int factorial(int n);
Stack contents
{
n 4
int f;
main() rtn adrs
if (n<1) return 1;
f ???
f=factorial(n-1);
n 3
// fact() return address
return n*f; fact() rtn adrs

} f ???
int main() n 2
{ stack ptr
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 48


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
Return address
int factorial(int n); Stack contents
{
n 4
int f;
main() rtn adrs
if (n<1) return 1;
f ???
f=factorial(n-1);
n 3
// fact() return address
return n*f; fact() rtn adrs

} f ???
int main() n 2
{ fact() rtn adrs
cout<<factorial(4)<<“ =4!”<<endl; stack ptr
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 49


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std;
Local variable
int factorial(int n); Stack contents
{
n 4
int f;
main() rtn adrs
if (n<1) return 1;
f ???
f=factorial(n-1);
n 3
// fact() return address
return n*f; fact() rtn adrs

} f ???
int main() n 2
{ fact() rtn adrs
cout<<factorial(4)<<“ =4!”<<endl; f ???
reutrn 0; stack ptr
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 50


Use of stack in Implementation of Recursive Algorithms (cont.)
Parameter
#include<iostream>
Stack contents
using namespace std;
n 4
int factorial(int n);
main() rtn adrs
{
f ???
int f;
n 3
if (n<1) return 1;
fact() rtn adrs
f=factorial(n-1);
f ???
// fact() return address
n 2
return n*f;
fact() rtn adrs
}
f ???
int main()
n 1
{
cout<<factorial(4)<<“ =4!”<<endl; stack ptr

reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 51


Use of stack in Implementation of Recursive Algorithms (cont.)
Return address
#include<iostream>
Stack contents
using namespace std;
n 4
int factorial(int n);
main() rtn adrs
{
f ???
int f;
n 3
if (n<1) return 1;
fact() rtn adrs
f=factorial(n-1);
f ???
// fact() return address
n 2
return n*f;
fact() rtn adrs
}
f ???
int main()
n 1
{
fact() rtn adrs
cout<<factorial(4)<<“ =4!”<<endl;
stack ptr
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 52


Use of stack in Implementation of Recursive Algorithms (cont.)
Local variable
#include<iostream>
Stack contents
using namespace std;
n 4
int factorial(int n);
main() rtn adrs
{
f ???
int f;
n 3
if (n<1) return 1;
fact() rtn adrs
f=factorial(n-1);
f ???
// fact() return address
n 2
return n*f;
fact() rtn adrs
}
f ???
int main()
n 1
{
fact() rtn adrs
cout<<factorial(4)<<“ =4!”<<endl;
f ???
reutrn 0;
} stack ptr

Data Structures Featuring C++ © Sridhar Radhakrishnan 53


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std; Stack contents
int factorial(int n);
n 4
{
main() rtn adrs
int f;
f ???
if (n<1) return 1;
n 3
f=factorial(n-1);
fact() rtn adrs
// fact() return address
f ???
return n*f;
} n 2

int main() fact() rtn adrs


{ f ???
cout<<factorial(4)<<“ =4!”<<endl; stack ptr 1
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 54


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std; Stack contents
int factorial(int n);
n 4
{
main() rtn adrs
int f;
f ???
if (n<1) return 1;
n 3
f=factorial(n-1);
fact() rtn adrs
// fact() return address
f ???
return n*f;
n 2
}
int main() fact() rtn adrs

{ f 1
cout<<factorial(4)<<“ =4!”<<endl; stack ptr 1
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 55


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std; Stack contents
int factorial(int n);
n 4
{
main() rtn adrs
int f;
f ???
if (n<1) return 1;
n 3
f=factorial(n-1);
fact() rtn adrs
// fact() return address
f ???
return n*f;
} stack ptr 2

int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 56


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std; Stack contents
int factorial(int n);
n 4
{
main() rtn adrs
int f;
f ???
if (n<1) return 1;
n 3
f=factorial(n-1);
fact() rtn adrs
// fact() return address
f 2
return n*f;
} stack ptr 2

int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 57


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std; Stack contents
int factorial(int n);
n 4
{
main() rtn adrs
int f;
f ???
if (n<1) return 1;
stack ptr 3
f=factorial(n-1);
// fact() return address
return n*f;
}
int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 58


Use of stack in Implementation of Recursive Algorithms (cont.)

#include<iostream>
using namespace std; Stack contents
int factorial(int n);
n 4
{
main() rtn adrs
int f;
f 6
if (n<1) return 1;
stack ptr 3
f=factorial(n-1);
// fact() return address
return n*f;
}
int main()
{
cout<<factorial(4)<<“ =4!”<<endl;
reutrn 0;
}

Data Structures Featuring C++ © Sridhar Radhakrishnan 59


Recursive Functions and Large Local Variables

• In this recursive version of factorial(), every time a recursive function calls itself,
another activation record is created.

• If the recursion continues too long, activation records can in time overfill the stack
and cause the program to crash.

• This is the normal fate of programs whose recursive procedures have no base cases.

• In case of excessively large local variables, stack can be overfilled even in the
absence of recursion.This is typically found in the case of very large arrays.

Data Structures Featuring C++ © Sridhar Radhakrishnan 60


Strategies for Algorithms
• Greedy Algorithms:
 A greedy algorithm works by making the decision that seems most promising at
any given time;
 It never reconsiders this decision.

• Divide-and-Conquer Algorithms:
 Divide-and-conquer is a top-down technique for designing algorithms.
 It first decomposes an instance of a problem into a number of smaller sub-
instances of the same problem, solving independently each of these sub-
instances and then merging the solutions to obtain the solution of the original
instance.

Data Structures Featuring C++ © Sridhar Radhakrishnan 61


Strategies for Algorithms (cont.)
• Backtracking:
 Backtracking is a top-dow n approach that typically uses a depth-first search
algorithm in its basic form.
 Examples of problems amenable to a backtracking strategy include:
 finding winning moves in games like chess and decision-
tree problems, and many optimization problems.

• Branch-and-Bound Algorithms:
 Branch-and-Bound is sim ilar to the back tracking technique but differs in the
mechanism used to select and explore vertices in the graph.
 A value is associated with exploring a vertex and if it this value is “promising” then
the vertex is explored.

Data Structures Featuring C++ © Sridhar Radhakrishnan 62


Strategies for Algorithms (cont.)
• Dynamic Programming:

 Dynamic programming is a bottom-up technique .


 We start by obtaining solutions to the smallest sub-instances and repeatedly
combine the solutions to get solutions for larger instances until finally we arrive at
the solution of the original instance.

Data Structures Featuring C++ © Sridhar Radhakrishnan 63

You might also like