0% found this document useful (0 votes)
0 views

D7 Recursive Problem Solving and Intro to Backtracking

The document discusses recursion and backtracking as essential programming concepts, explaining how recursion involves functions calling themselves to solve problems through base and recursive cases. It provides examples of recursive algorithms for calculating factorials, generating Fibonacci sequences, and removing adjacent duplicates, as well as outlining the backtracking technique for solving problems like maze navigation and the N-Queens problem. Additionally, it emphasizes the importance of optimizing recursive solutions with memoization and the practical applications of these techniques in various fields.
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
0 views

D7 Recursive Problem Solving and Intro to Backtracking

The document discusses recursion and backtracking as essential programming concepts, explaining how recursion involves functions calling themselves to solve problems through base and recursive cases. It provides examples of recursive algorithms for calculating factorials, generating Fibonacci sequences, and removing adjacent duplicates, as well as outlining the backtracking technique for solving problems like maze navigation and the N-Queens problem. Additionally, it emphasizes the importance of optimizing recursive solutions with memoization and the practical applications of these techniques in various fields.
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 30

Recursive Problem-

Solving &
Backtracking

Ashwin Perti
What is Recursion?

Recursion is a fundamental programming concept where a


function calls itself (Directly or Indirectly) to solve
problems.

It's a powerful tool that solves complex problems by


breaking them into smaller, manageable subproblems,
involving two main components: the base case and the
recursive case.
Key Concepts in Recursion

1 Base Case 2 Recursive Case


The condition that terminates the The condition where the function calls itself
recursion, preventing indefinite function with modified input, moving towards the
calls. base case.

3 Call Stack 4 Visual Representation


Mechanism that keeps track of function Illustrate the recursive call stack for a
calls, adding a new frame for each simple example, aiding comprehension.
recursive call.
Recipe for Writing Recursive Functions
Thinking of recursive algorithms can be tricky at first. Here's a recipe you can follow
that might help.
1. Write an if statement (Why?)
2 cases: base (may be more than one base case) and recursive

2. Handle simplest case - the base case(s) No recursive call needed (that’s why it is
the base case!)

3. Write the recursive call Input to call must be slightly simpler/smaller to move
towards the base case

4. Be optimistic: Assume the recursive call works!


Ask yourself: What does it do?
Ask yourself: How does it help?
Recursive Problem-Solving Exercises

1 Exercise 1: Compute Factorial

Walk through the recursive algorithm to calculate factorial.

2 Exercise 2: Print Fibonacci Sequence

Explain how the Fibonacci sequence can be generated using recursion.

3 Exercise 3: Recursively remove all adjacent duplicates

Demonstrate how to remove adjacent redundancy from a string recursively.


Recursive Problem-Solving Exercises

1 Exercise 1: Compute Factorial

Walk through the recursive algorithm to calculate factorial. long factorial(int n)


{
if (n == 0)
return 1;
2 Exercise 2: Print Fibonacci Sequence
else
Explain how the Fibonacci sequence can be generated using recursion. return(n * factorial(n-1));
}

3 Exercise 3: Recursively remove all adjacent duplicates

Demonstrate how to remove adjacent redundancy from a string recursively.


Recursive Problem-Solving Exercises
int fibbonacci(int n) {
if(n == 0){
1 Exercise 1: Compute Factorial return 0;
} else if(n == 1) {
Walk through the recursive algorithm to calculate factorial. return 1;
} else {
return (fibbonacci(n-1) + fibbonacci(n-2));
}
}
2 Exercise 2: Print Fibonacci Sequence

Explain how the Fibonacci sequence can be generated using recursion.

3 Exercise 3: Recursively remove all adjacent duplicates

Demonstrate how to remove adjacent redundancy from a string recursively.


Recursive Problem-Solving Exercises

1 Exercise 1: Compute Factorial

Walk through the recursive algorithm to calculate factorial.

2 Exercise 2: Print Fibonacci Sequence

Explain how the Fibonacci sequence can be generated using recursion.

3 Exercise 3: Recursively remove all adjacent duplicates

Demonstrate how to remove adjacent redundancy from a string recursively.


char* removeUtil(char* str, char* last_removed)

Recursive Problem-Solving
{
// If length of string is 1 or 0
if (str[0] == '\0' || str[1] == '\0')

Exercises
return str;

// Remove leftmost same characters and recur for remaining string


if (str[0] == str[1]) {
*last_removed = str[0];
while (str[1] && str[0] == str[1])
str++;
1 Exercise 1: Compute Factorial str++;
return removeUtil(str, last_removed);
}
Walk through the recursive algorithm to calculate factorial. /* At this point, the first character is definitely different from its
adjacent. Ignore first character and recursively remove characters from
remaining string*/
char* rem_str = removeUtil(str + 1, last_removed);

/* Check if the first character of the rem_string matches with the first
character of the original string */

2 Exercise 2: Print Fibonacci Sequence if (rem_str[0] && rem_str[0] == str[0]) {


*last_removed = str[0];

Explain how the Fibonacci sequence can be generated using recursion. // Remove first character
return (rem_str + 1);
}
/* If remaining string becomes empty and last removed character is same as
first character of original string. This is needed for a string like
"acbbcddc“ */
if (rem_str[0] == '\0' && *last_removed == str[0])
return rem_str;
3 Exercise 3: Recursively remove all adjacent duplicates
/* If the two first characters of str and rem_str don't match, append first
character of str before the first character of rem_str. */
Demonstrate how to remove adjacent redundancy from a string rem_str--;
rem_str[0] = str[0];
recursively. return rem_str;
}
// Function to remove
char* removes(char* str){
char last_removed = '\0';
return removeUtil(str, &last_removed);
Example: How number of nodes in a binary tree can be calculated using recursion?

int nodeCount(struct node* node)


{
if (node==NULL)
return 0;
else
return(nodeCount(node->left) + 1 + nodeCount(node-
>right));
}
Example: How number of leaves in a binary tree can be calculated using recursion?

unsigned int getLeafCount(struct node* node)


{
if(node == NULL)
return 0;
if(node->left == NULL && node->right==NULL)

return 1;
else
return getLeafCount(node->left) +
getLeafCount(node->right);
}
Example: How a binary tree can be converted into its mirror form using recursion?

void mirror(struct node* node)


{
if (node==NULL)
return;
else
{
struct node* temp;
/* swap the pointers in this node */
Temp = node->left;
node->left = node->right;
node->right = temp;
/* call for subtrees */
mirror(node->left);
mirror(node->right);
}
}
Tips for Recursive Problem-Solving

1 Identify Base Cases 2 Understand the Problem

Clearly define the conditions that stop the Break down the problem into smaller parts
recursion, ensuring efficient coding. and comprehend their relationships for
effective problem-solving.

3 Test with Small Inputs 4 Optimize with Memoization

Validate recursive solutions with If applicable, cache intermediate results


small inputs before tackling larger to avoid redundant calculations and
ones for accurate results. optimize performance.
Optimize with Memoization (Dynamic Programming)

#include <stdio.h> int main() {


#include <stdlib.h> // Example usage
int n = 5;
// Function to calculate Fibonacci numbers with memoization
int fibonacci(int n, int* memo) { // Initialize memoization table with -1 (indicating that values are not
// Base case computed yet)
if (n <= 1) { int* memo = (int*)malloc((n + 1) * sizeof(int));
return n; for (int i = 0; i <= n; i++) {
} memo[i] = -1;
}
// Check if result is already in the memo
if (memo[n] != -1) { // Calculate and print the result
return memo[n]; int result = fibonacci(n, memo);
} printf("Fibonacci(%d) = %d\n", n, result);

// Perform the computation // Free allocated memory


int result = fibonacci(n - 1, memo) + fibonacci(n - 2, memo); free(memo);

// Store the result in the memo return 0;


memo[n] = result; }

return result;
}
Tail Recursion
A recursive function is tail recursive when recursive call is the last thing executed by the function.

void printFun(test)
{
if (test<1)
return;
else
{
printf("%d", test);
// no statement to execute after recursive call
printFun(test-1);
return;
}
}

long factorial(int n)
{
if (n == 0)
return 1; Is it also TR?
else
return(n * factorial(n-1));
}
•Mean of Array using Recursion
•Sum of natural numbers using recursion
•Decimal to binary number using recursion
•Sum of array elements using recursion
•Print reverse of a string using recursion
•Program for length of a string using recursion
•Sum of digit of a number using recursion
•Tail recursion to calculate sum of array elements.
•Program to print first n Fibonacci Numbers
•Program for factorial of a number
•Recursive Programs to find Minimum and Maximum elements of
array
•Recursively remove all adjacent duplicates
•Sort the Queue using Recursion
•Reversing a queue using recursion
•Delete a linked list using recursion
•Length of longest palindromic sub-string : Recursion
•Program for Tower of Hanoi Algorithm
•Find geometric sum of the series using recursion
•Convert a String to an Integer using Recursion
•DFS traversal of a Tree
•How to Sort a Stack using Recursion
•Reverse a Doubly linked list using recursion
•Check if a string is a scrambled form of another string
•N Queen Problem
•Algorithm to Solve Sudoku
Backtracking: Idea
• Backtracking is a technique used to solve problems with a large search
space, by systematically trying and eliminating possibilities.
• A standard example of backtracking would be going through a maze.
– At some point, you might have two options of which direction to go:

Portion A
Junction

Portion B
One strategy would be to try going through Portion A of
the maze. o n
n cti
J u
If you get stuck before you find your way out, then
you "backtrack" to the junction.
Portion B

At this point in time you know that Portion A will NOT


lead you out of the maze,

Portion A
so you then start searching in Portion B
Backtracking

• Clearly, at a single junction you could


have even more than 2 choices.

• The backtracking strategy says to try


each choice, one after the other,
on
– if you ever get stuck, "backtrack" to the
nc ti
junction and try the next choice. Ju C
B
A
• If you try all choices and never found
a way out, then there is no solution to
the maze.
Backtracking-Simulation

dead end
?
dead end
dead end

?
start ? ?
dead end
dead end

success!
Backtracking

• Dealing with the maze:


– From your start point, you will iterate through each possible
starting move.
– From there, you recursively move forward.
– If you ever get stuck, the recursion takes you back to where
you were, and you try the next possible move.

• Make sure you don't try too many possibilities,


– Mark which locations in the maze have been visited already
so that no location in the maze gets visited twice.
– If a place has already been visited, there is no point in trying
to reach the end of the maze from there again.
Backtracking- Key Points
• The neat thing about coding up backtracking is that it can be done recursively, without
having to do all the bookkeeping at once.

• Instead, the stack of recursive calls does most of the bookkeeping (i.e., keeps track of which
locations we’ve tried so far.)

• Backtracking is really just depth-first search on an implicit graph of configurations.

• Backtracking can easily be used to iterate through all subsets or permutations of a set.

• Backtracking ensures correctness by enumerating all possibilities.

• For backtracking to be efficient, we must prune the search space.


Backtracking Algorithm for Rat in a Maze

Define Recursive Function Explore Possible Moves

Define a function that explores paths and Discuss how the algorithm tries moving in
backtracks when necessary. all possible directions to find the right path.

Mark the Path and Backtrack Visual Representation

Emphasize the importance of marking the Show step-by-step visuals of the algorithm
path and undoing marks during in action to aid understanding.
backtracking for methodical navigation.
Rat in a Maze - Code Snippet
Consider a rat placed at (0, 0) in a square matrix of order N * N. It has to reach
the destination at (N – 1, N – 1). Find all possible paths that the rat can take to
reach from source to destination. The directions in which the rat can move
are ‘U'(up), ‘D'(down), ‘L’ (left), ‘R’ (right). Value 0 at a cell in the matrix
represents that it is blocked and rat cannot move to it while value 1 at a cell in
the matrix represents that rat can be travel through it.
Step-by-step approach:
• Create isValid() function to check if a cell at position (r, c) is
inside the maze and unblocked.
• Create findPath() to get all valid paths:
• Base case: If the current position is the bottom-right cell,
add the current path to the result and return.
• Mark the current cell as blocked.
• Iterate through all possible directions.
• Calculate the next position based on the current
direction.
• If the next position is valid (i.e, if isValid() return true),
append the direction to the current path and recursively
call the findPath() function for the next cell.
• Backtrack by removing the last direction from the
current path.
Applications of Maze Solving Approach
1.Robotics:
Autonomous robots often use maze-solving algorithms to navigate through unknown
environments. This is crucial for tasks like exploration, search and rescue missions, and automated
warehouse systems.
2.Computer Games/ Puzzle Solving :
Maze-solving algorithms are frequently used in the development of computer games, particularly in
designing levels and creating challenging environments for players.
3.Path Planning/ Network Routing :
Beyond mazes, these algorithms are applied to more complex path-planning problems in fields
such as logistics, transportation, and traffic management and to find the most efficient path for
data transmission in a network.
4.Artificial Intelligence and Machine Learning:
Maze-solving problems are often used as benchmarks or exercises in AI and machine learning
courses. Reinforcement learning algorithms, for example, can be trained to solve mazes as part
of their learning process.
5.Maze Generation:
Algorithms used for maze solving can also be applied to generate interesting and challenging
mazes for entertainment purposes, such as in video games or puzzles.
6.Telecommunications:
Maze-solving principles are applicable in optimizing the routing of signals in telecommunications
networks, helping to ensure efficient data transmission.
N Queens Problem
1.Base Case: In the N-Queens problem, the base case occurs when all queens are
successfully placed on the board without conflicting with each other.

2.Constraint Satisfaction: The constraint is that no two queens can share the
same row, column, or diagonal. Ensuring that each queen is placed in a position that
satisfies these constraints is crucial.

3.Decision Space: At each step, we need to decide the row where the next queen
will be placed. The decision space consists of the remaining rows that are not
threatened by the already placed queens.

4.Solution Space: The solution space for the N-Queens problem is the set of all
possible arrangements of queens on the chessboard. The size of the solution space
is N! since there are N choices for the queen's position in the first row, N−1 choices for
the second row, and so on.

5.Optimization: Backtracking can be optimized by pruning the search space


whenever a conflict is detected. If placing a queen at a certain position leads to a
conflict, there's no need to explore further from that position.
N Queens Problem
6.Loop Conditions: The backtracking algorithm should iterate over all possible
positions for placing the next queen within the constraints of the problem.

7.Resource Constraints: The N-Queens problem may have resource constraints


such as memory usage (especially for large N) and time complexity. Efficient
data structures and pruning techniques help manage these constraints.

8.Corner Cases: Corner cases in the N-Queens problem may include scenarios where
N=0 or N=1, where there are no valid solutions since there are no queens to place
or only one queen on the board, respectively.

9.Backtracking Strategies: In the N-Queens problem, depth-first search (DFS) is


commonly used, where we explore each possible position for the next queen
recursively until a solution is found or all possibilities are exhausted.

10.Early Termination: If we detect that it's impossible to place N queens on the board
due to conflicts, we can terminate the backtracking process early, saving
computational resources.
1) Start in the leftmost column
2) If all queens are placed
return true
3) Try all rows in the current column. Do following
for every tried row.
a) If the queen can be placed safely in this row
then mark this [row, column] as part of the
solution and recursively check if placing
queen here leads to a solution.
b) If placing queen in [row, column] leads to a
solution then return true.
c) If placing queen doesn't lead to a solution
then unmark this [row, column] (Backtrack)
and go to step (a) to try other rows.
4) If all rows have been tried and nothing worked,
return false to trigger backtracking.
https://dipeshpatil.github.io/algorithms-visualiser/#/

https://visualgo.net/en

https://algorithm-visualizer.org/

https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

You might also like