0% found this document useful (0 votes)
55 views53 pages

3 - Recursion

Recursion is a fundamental concept in computer science that allows problems to be broken down into smaller subproblems. A recursive program calls itself to solve subproblems, which are smaller instances of the overall problem. Base cases provide terminating conditions, and subproblem solutions are combined to solve the overall problem. Examples demonstrate using recursion to sum the squares of integers in a range and to calculate factorials in both a "going down" and "combining halves" manner. Call trees and traces can depict the recursive calls graphically.

Uploaded by

Ioannis
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
55 views53 pages

3 - Recursion

Recursion is a fundamental concept in computer science that allows problems to be broken down into smaller subproblems. A recursive program calls itself to solve subproblems, which are smaller instances of the overall problem. Base cases provide terminating conditions, and subproblem solutions are combined to solve the overall problem. Examples demonstrate using recursion to sum the squares of integers in a range and to calculate factorials in both a "going down" and "combining halves" manner. Call trees and traces can depict the recursive calls graphically.

Uploaded by

Ioannis
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 53

Recursion

Manolis Koubarakis

Data Structures and Programming


1
Techniques
Recursion
• Recursion is a fundamental concept of
Computer Science.
• It usually help us to write simple and elegant
solutions to programming problems.
• You will learn to program recursively by
working with many examples to develop your
skills.

Data Structures and Programming


2
Techniques
Recursive Programs
• A recursive program is one that calls itself in
order to obtain a solution to a problem.
• The reason that it calls itself is to compute a
solution to a subproblem that has the following
properties:
– The subproblem is smaller than the problem to be
solved.
– The subproblem can be solved directly (as a base
case) or recursively by making a recursive call.
– The subproblem’s solution can be combined with
solutions to other subproblems to obtain a solution
to the overall problem.
Data Structures and Programming
3
Techniques
Example
• Let us consider a simple program to add up all the
squares of integers from m to n.

• An iterative function to do this is the following:

int SumSquares(int m, int n)


{
int i, sum;

sum=0;
for (i=m; i<=n; ++i) sum +=i*i;
return sum;
}

Data Structures and Programming


4
Techniques
Recursive Sum of Squares
int SumSquares(int m, int n)
{
Recursive call
if (m<n) {
return m*m + SumSquares(m+1, n);
} else {
return m*m;
}
} Base case

Data Structures and Programming


5
Techniques
Comments
• In the case that the range m:n contains more than one
number, the solution to the problem can be found by
adding (a) the solution to the smaller subproblem of
summing the squares in the range m+1:n and (b) the
solution to the subproblem of finding the square of m. (a) is
then solved in the same way (recursion).

• We stop when we reach the base case that occurs when


the range m:n contains just one number, in which case
m==n.

• This recursive solution can be called “going-up” recursion


since the successive ranges are m+1:n, m+2:n etc.

Data Structures and Programming


6
Techniques
Going-Down Recursion
int SumSquares(int m, int n)
{
Recursive call
if (m<n) {
return SumSquares(m, n-1) + n*n;
} else {
return n*n;
}
} Base case

Data Structures and Programming


7
Techniques
Recursion Combining Two Half-
Solutions
int SumSquares(int m, int n)
{
int middle;

if (m==n) {
return m*m; Base case
} else {
middle=(m+n)/2;
return
SumSquares(m,middle)+SumSquares(middle+1,n);
}
}
Recursive call Recursive call
Data Structures and Programming
8
Techniques
Comments
• The recursion here says that the sum of the squares of the
integers in the range m:n can be obtained by adding the
sum of the squares of the left half range, m:middle, to
the sum of the squares of the right half range,
middle+1:n.

• We stop when we reach the base case that occurs when


the range contains just one number, in which case m==n.

• The middle is computed by using integer division (operator


/) which keeps the quotient and throws away the
remainder.

Data Structures and Programming


9
Techniques
Call Trees and Traces
• We can depict graphically the behaviour of
recursive programs by drawing call trees or
traces.

Data Structures and Programming


10
Techniques
Call Trees
SumSquares(5,10)

SumSquares(5,7) SumSquares(8,10)

SumSquares(5,6) SumSquares(7,7)SumSquares(8,9) SumSquares(10,10)

SumSquares(8,8) SumSquares(9,9)
SumSquares(5,5) SumSquares(6,6)

Data Structures and Programming


11
Techniques
Annotated Call Trees
355
SumSquares(5,10)

110 245
SumSquares(5,7) SumSquares(8,10)

61 145
SumSquares(5,6) SumSquares(7,7)SumSquares(8,9) SumSquares(10,10)
49 100

SumSquares(8,8) SumSquares(9,9)
SumSquares(5,5) SumSquares(6,6) 81
64
25 36
Data Structures and Programming
12
Techniques
Traces
SumSquares(5,10)=SumSquares(5,7)+SumSquares(8,10)=
=SumSquares(5,6)+SumSquares(7,7)
+SumSquares(8,9)+SumSquares(10,10)
=SumSquares(5,5)+SumSquares(6,6)
+SumSquares(7,7)
+SumSquares(8,8)+SumSquares(9,9)
+SumSquares(10,10)
=((25+36)+49)+((64+81)+100)
=(61+49)+(145+100)
=(110+245)
=355

Data Structures and Programming


13
Techniques
Computing the Factorial
• Let us consider a simple program to compute the
factorial n! of n.

• An iterative function to do this is the following:

int Factorial(int n)
{
int i, f;

f=1;
for (i=2; i<=n; ++i) f*=i;
return f;
}

Data Structures and Programming


14
Techniques
Recursive Factorial
int Factorial(int n)
{
if (n==1) {
Base case
return 1;
} else {
return n*Factorial(n-1);
}
Recursive call
}

Data Structures and Programming


15
Techniques
Computing the Factorial (cont’d)
• The previous program is a “going-down”
recursion.
• Can you write a “going-up” recursion for
factorial?
• Can you write a recursion combining two half-
solutions?
• The above tasks do not appear to be easy.

Data Structures and Programming


16
Techniques
Computing the Factorial (cont’d)
• It is easier to first write a function
Product(m,n) which multiplies together
the numbers in the range m:n.
• Then Factorial(n)=Product(1,n).

Data Structures and Programming


17
Techniques
Multiplying m:n Together Using Half-
Ranges
int Product(int m, int n)
{
int middle;

if (m==n) {
return m; Base case
} else {
middle=(m+n)/2;
return Product(m,middle)*Product(middle+1,n);
}
}
Recursive call
Recursive call
Data Structures and Programming
18
Techniques
Reversing a Linked List

Airport Link Airport Link


.
Airport Link

A DUS ORD SAN

Data Structures and Programming


19
Techniques
The Result

Airport Link Airport Link


.
Airport Link

A: SAN ORD DUS

Data Structures and Programming


20
Techniques
Reversing a Linked List
• Let us now writing a function for reversing a
linked list L.
• The type NodeType has been defined in the
previous lecture as follows:

typedef char AirportCode[4];


typedef struct NodeTag {
AirportCode Airport;
struct NodeTag *Link;
} NodeType;

Data Structures and Programming


21
Techniques
Reversing a List Iteratively
• An iterative function for reversing a list is the following:

void Reverse(NodeType **L)


{
NodeType *R, *N, *L1;

L1=*L;
R=NULL;
while (L1 != NULL) {
N=L1;
L1=L1->Link;
N->Link=R;
R=N;
}
*L=R;
}

Data Structures and Programming


22
Techniques
Before the while Loop
L1=*L;
R=NULL;

Airport Link Airport Link Airport Link

*L SAN ORD DUS

L .
L1 R

Data Structures and Programming


23
Techniques
After the First Execution of the while
Loop
while (L1 != NULL) {
N=L1;
L1=L1->Link;
N->Link=R;
R=N;
}

Airport Link Airport Link


.
Airport Link
*L SAN . ORD DUS

N L1 R

Data Structures and Programming


24
Techniques
After the Second Execution of the
while Loop
while (L1 != NULL) {
N=L1;
L1=L1->Link;
N->Link=R;
R=N;
}

Airport Link Airport Link Airport Link

*L SAN . ORD DUS .


L
N L1 R

Data Structures and Programming


25
Techniques
After the Third Execution of the
while Loop
while (L1 != NULL) {
N=L1;
L1=L1->Link;
N->Link=R;
R=N;
}

Airport Link Airport Link Airport Link

*L SAN . ORD DUS

L .
N L1 R

Data Structures and Programming


26
Techniques
After the while Loop Terminates
*L=R;

Airport Link Airport Link Airport Link

*L SAN . ORD DUS

L .
N L1 R

Data Structures and Programming


27
Techniques
Question
• If in our main program we have a list with a
pointer A to its first node, how do we call the
previous function?

Data Structures and Programming


28
Techniques
Answer
• We should make the following call:
Reverse(&A)

Data Structures and Programming


29
Techniques
Example
• Let us now call Reverse(&A) for the
following list.

Airport Link Airport Link


.
Airport Link

A DUS ORD SAN

Data Structures and Programming


30
Techniques
The Resulting List

Airport Link Airport Link


.
Airport Link

A: SAN ORD DUS

Data Structures and Programming


31
Techniques
Reversing Linked Lists (cont’d)
• A recursive solution to the problem of
reversing a list L is found by partitioning the
list into its head Head(L) and tail Tail(L)
and then concatenating the reverse of
Tail(L) with Head(L).

Data Structures and Programming


32
Techniques
Head and Tail of a List
• Let L be a list. Head(L) is a list
containing the first node of L. Tail(L) is a
list consisting of L’s second and succeeding
nodes.
• If L==NULL then Head(L) and Tail(L)
are not defined.
• If L consists of a single node then Head(L) is
the list that contains that node and Tail(L)
is NULL.

Data Structures and Programming


33
Techniques
Example
• Let L=(SAN, ORD, BRU, DUS). Then
Head(L)=(SAN) and
Tail(L)=(ORD, BRU, DUS).

Data Structures and Programming


34
Techniques
Reversing Linked Lists (cont’d)
NodeType *Reverse(NodeType *L)
{
NodeType *Head, *Tail;

if (L==NULL) {
return NULL;
} else {
Partition(L, &Head, &Tail);
return Concat(Reverse(Tail), Head);
}
}

Data Structures and Programming


35
Techniques
Reversing Linked Lists: Partitioning the
List into Head and Tail
void Partition(NodeType *L, NodeType **Head,
NodeType **Tail)
{
if (L != NULL) {
*Tail=L->Link;
*Head=L;
(*Head)->Link=NULL;
}
}

Data Structures and Programming


36
Techniques
Example
• Let us execute Partition(L, &Head,
&Tail) for the following list.

Airport Link Airport Link


.
Airport Link

L DUS ORD SAN

Data Structures and Programming


37
Techniques
Example (cont’d)
*Tail=L->Link;
*Head=L;
(*Head)->Link=NULL;

Airport Link Airport Link


.
Airport Link

L DUS . ORD SAN

Head* Tail*

Head Tail
Data Structures and Programming
38
Techniques
Reversing Linked Lists: Concatenation
NodeType *Concat(NodeType *L1, NodeType *L2)
{
NodeType *N;

if (L1 == NULL) {
return L2;
} else {
N=L1;
while (N->Link != NULL) N=N->Link;
N->Link=L2;
return L1;
}
}

Data Structures and Programming


39
Techniques
Example
• Let us execute Concat(L1,L2) for the
following two lists

Airport Link Airport Link


.
Airport Link
L1 SAN . ORD SAN
Link

L2

Data Structures and Programming


40
Techniques
The Resulting List

Airport Link Airport Link


.
Airport Link
L1 SAN ORD SAN

L2

Data Structures and Programming


41
Techniques
Infinite Regress
• Let us consider again the recursive factorial function:
int Factorial(int n);
{
if (n==1) {
return 1;
} else {
return n*Factorial(n-1);
}
}

• What happens if we call Factorial(0)?

Data Structures and Programming


42
Techniques
Infinite Regress (cont’d)
Factorial(0)= 0 * Factorial(-1)
= 0 * (-1) * Factorial(-2)
= 0 * (-1) * Factorial(-3)
and so on, in an infinite regress.

When we execute this function call, we get


“Segmentation fault (core dumped)”.

Data Structures and Programming


43
Techniques
The Towers of Hanoi

1 2 3

Data Structures and Programming


44
Techniques
The Towers of Hanoi (cont’d)
• To Move 4 disks from Peg 1 to Peg 3:
– Move 3 disks from Peg 1 to Peg 2
– Move 1 disk from Peg 1 to Peg 3
– Move 3 disks from Peg 2 to Peg 3

Data Structures and Programming


45
Techniques
Move 3 Disks from Peg 1 to Peg 2

1 2 3

Data Structures and Programming


46
Techniques
Move 1 Disk from Peg 1 to Peg 3

1 2 3

Data Structures and Programming


47
Techniques
Move 3 Disks from Peg 2 to Peg 3

1 2 3

Data Structures and Programming


48
Techniques
Done!

1 2 3

Data Structures and Programming


49
Techniques
A Recursive Solution
void MoveTowers(int n, int start, int finish, int spare)
{
if (n==1){
printf(“Move a disk from peg %1d to peg %1d\n”, start,
finish);
} else {
MoveTowers(n-1, start, spare, finish);
printf(“Move a disk from peg %1d to peg %1d\n”, start,
finish);
MoveTowers(n-1, spare, finish, start);
}
}

Data Structures and Programming


50
Techniques
Analysis
• Let us now compute the number of moves
L(n) that we need as a function of the
number of disks n:
L(1)=1
L(n)=L(n-1)+1+L(n-1)=2*L(n-1)+1, n>1

The above are called recurrence relations. They can


be solved to give:
L(n)=2n-1

Data Structures and Programming


51
Techniques
Analysis (cont’d)
• Techniques for solving recurrence relations are
taught in the Algorithms and Complexity
course.

• The running time of algorithm MoveTowers


is exponential in the size of the input.

Data Structures and Programming


52
Techniques
Readings
• T. A. Standish. Data structures, algorithms and
software principles in C.
Chapter 3.
• (προαιρετικά) R. Sedgewick. Αλγόριθμοι σε C.
Κεφ. 5.1 και 5.2.

Data Structures and Programming


53
Techniques

You might also like