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

Recursion II - No Code

The document outlines a lecture on advanced recursion techniques, focusing on backtracking and divide and conquer strategies. It includes topics such as time and space complexity, common pitfalls, and practical applications with examples like combinations and fair distribution of cookies. Additionally, it emphasizes the importance of pruning in backtracking and provides practice questions for further learning.

Uploaded by

remidan37
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Recursion II - No Code

The document outlines a lecture on advanced recursion techniques, focusing on backtracking and divide and conquer strategies. It includes topics such as time and space complexity, common pitfalls, and practical applications with examples like combinations and fair distribution of cookies. Additionally, it emphasizes the importance of pruning in backtracking and provides practice questions for further learning.

Uploaded by

remidan37
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 68

Recursion II

Lecture Flow

1) Pre-requisites
2) Review of previous lecture
3) Backtracking
4) Time and space complexity
5) Things to Pay Attention(common pitfalls)
6) Divide and Conquer
7) Practice questions
8) Resources
9) Quote of the day
Pre-requisites
● Recursion I
● Time and Space Complexity Analysis
Revision
Instances where you have seen recursion useful from last lecture
and practice?
● Unidentified number of levels
○ Decode String
● Input is expressed using recursive rules explicitly or implicitly
○ Fibonacci
○ Pascal’s Triangles
○ Find Kth Bit in Nth Binary String
● Complete Search Problems
○ Predict the Winner
● Tree Structure Traversal/Tree related problems
○ Lowest common ancestor of a binary tree
○ Validate BST
Let’s visualize fibonacci numbers
Link
Is that all recursion can do?
Today, we will add more areas
where recursion shines
Backtracking
How would you find your way out?
How would you find your way out?
● Let’s put peanuts on our path
● When you reach a dead end, go
back to where you came from
and
● Explore a different path

● Red peanut to mark we have


been here
● Orange peanut currently in
exploring path
How would you find your way out?
● Let’s put peanuts on our path
● When you reach a dead end, go
back to where you came from
and
● Explore a different path

● Red peanut to mark we have


been here
● Orange peanut currently in
exploring path
How would you find your way out?
● Let’s put peanuts on our path
● When you reach a dead end, go
back to where you came from
and
● Explore a different path

● Red peanut to mark we have


been here
● Orange peanut currently in
exploring path
For our maze our candidates at each cell where
● Go forward
● Take right Turn
● Take left Turn
● Go backward?
How would we simulate this with code?
Template

def backtrack(candidate):
if candidate is a solution:
# process or output a candidate
return

# handle basecases

# iterate all possible candidates.


for next_candidate in list_of_candidates:
if next_candidate is valid:
# try this partial candidate solution
place(next_candidate)

# given the candidate, explore further.


backtrack(next_candidate)

# backtrack
remove(next_candidate)
What are the next candidates for this state?

S Legend

S - Start
N - Next
E
E - End
S

S N S

E E
S

S N S

E E

S N N N N S N

N N

E E N E E
S

S N S

E E

S N N N N S N

N N

E E N E E

S N N S N N S S

N N

E E N N E N E
S S

N N

N N E N N E
Reflection
● How do we describe one candidate?
○ i, j, grid; where (i, j) is our current position
● Why can’t the next state be our caller’s state?
● What is the base case?
Backtracking is a method to solve problems by
trying out different options and exploring all
possible paths until a solution is found.
If it reaches a dead end, it goes back to
the last decision point and tries another
option until all possibilities have been
exhausted.
Types of backtracking

● Decision Problem
○ In this, we search for a feasible solution.
● Optimization Problem
○ In this, we search for the best solution.
● Enumeration Problem
○ In this, we find all feasible solutions. (Permutation and Combination)
Use the backtracking template to solve the
following problem
Combinations
nums = [1,2,3] | k = 2 <> [[1,2],[1,3],[2,3]]

i=0,
com=[]

What are the


different calls at this
level?
nums = [1,2,3] | k = 2 <> [[1,2],[1,3],[2,3]]

i=0,
com=[]

insert no insert

i=1, i=1,
com=[1] com=[]

What are the


different calls at this
level?
nums = [1,2,3] | k = 2 <> [[1,2],[1,3],[2,3]]

i=0,
com=[]

insert no insert

i=1, i=1,
com=[1] com=[]

insert no insert insert no insert

i=2, i=2, i=2, i=2,


com=[1,2] com=[1] com=[2] com=[]

What are the


different calls at this
level?
nums = [1,2,3] | k = 2 <> [[1,2],[1,3],[2,3]]
i=0,
com=[]

insert no insert

i=1, i=1,
com=[1] com=[]

insert no insert
insert no insert
i=2,
com=[1,2] i=2, i=2, i=2,
com=[1] com=[2] com=[]
Add [1,2]
insert no insert insert no insert insert no insert

i=3, i=3, i=3,


com=[1,3] i=3, i=3, i=3,
com=[1] com=[2,3] com=[2] com=[3] com=[]
Add [1,3] Dead end Add [2,3] Dead end Dead end Dead end
Time Complexity
● We have two branches
● We have a depth of size n
● When ever we find an answer we add a list of size K to our answer.

Time for the recursion = branches ^ depth


Time for the list addition = count of combinations * K = [n! / (K!) * (n-K)!] * k

O(2^n + {n! / [(K!) * (n-K)!]} * K)


Space Complexity
● We have the call stack
● Note: The array is pass by reference so it won’t count as function space
cost

Space Complexity = Depth * Space of function and arguments

O (n*s)
What type of backtracking was
the previous problem?
Splitting a String into Descending Consecutive values
num = “050043” 050043

0 50043 05 0043 050 043 0500 43 05004 3 05004 3

0 5 0043 0 50 043 0 50 043 0 500 43 0 5004 3 0 50043

0 5 0 043 0 5 00 43 0 5 004 3 0 5 0043

0 5 0 0 43 0 5 0 04 3 0 5 0 043

050043 0 5 0 0 43
Time Complexity Space Complexity
● We have a depth of n ● Size of the call stack
● At each step we have a choice ● The array we are keeping:
of n
Note: The array is global.

Time Complexity = O (n * (2 ^ n))


Space Complexity = O(n + n) = O(n)
What type of backtracking was
the previous problem?
Decision Problem: we were trying to find at
least one solution
How would we approach an
optimization problem using
backtracking?
How would we approach an optimization problem
using backtracking?
● Do the same thing we did for enumeration
● From all the solution, we pick the optimal solution
Fair Distribution of Cookies
Can you draw what the possible
branching would look like before you
go to implementation?
cookie= [1,2,3] | bucket = []
i=0,
bucket=[0,0]

What are the


different calls at this
level?
cookie= [1,2,3] | bucket = []
i=0,
bucket=[0,0]

put at put at 1
0
i=1, i=1,
bucket=[1,0] bucket=[0,1]

What are the


different calls at this
level?
cookie= [1,2,3] | bucket = []
i=0,
bucket=[0,0]

put at put at 1
0
i=1, i=1,
bucket=[1,0] bucket=[0,1]

put at put at 1
0 Put at 0 Put at 1
i=2,
bucket=[3,0] i=2, i=2, i=2,
bucket=[1,2] bucket=[2,1] bucket=[0,3]

What are the


different calls at this
level?
cookie= [1,2,3] | bucket = []
i=0,
bucket=[0,0]

put at put at 1
0
i=1, i=1,
bucket=[1,0] bucket=[0,1]

put at put at 1
0 Put at 0 Put at 1
i=2,
bucket=[3,0] i=2, i=2, i=2,
bucket=[1,2] bucket=[2,1] bucket=[0,3]
Put at Put at 1 Put at
0 Put at 1 Put at Put at 1 Put at Put at 1
0 0 0
i=3,
i=3, bucket=[1,3] i=3, i=3, i=3,
bucket=[4,2] bucket=[5,1] bucket=[2,4] bucket=[0,6]
i=3, i=3,
i=3, bucket=[3,3]
bucket=[6,0] bucket=[3,3]
Time Complexity Space Complexity

● Depth of the recursion tree ● Size of call stack


● Number of branches ● The bucket list

Time complexity = Branches(Depth) Note: The list is passed by reference

= O(Kn)
Space Complexity = O(n + k) = O(n)
Some of the paths will eventually lead to
a dead end or it is not optimal anymore

Shouldn't we just stop searching that path?


Pruning the execution tree
● What if we added more bases cases?
● Cases to prune
○ When path is not optimal anymore (check previous problem for instance)
○ Path eventually leads to dead end
● Stating facts before writing might help in discovering pruning cases
Optimize the previous solutions using pruning

Combinations
Optimize the previous solutions using pruning

Splitting a String into Descending Consecutive values


num = “050043” 050043

0 50043 05 0043 050 043 0500 43 05004 3 05004 3

0 5 0043 0 50 043 0 50 043 0 500 43 0 5004 3 0 50043

0 5 0 043 0 5 00 43 0 5 004 3 0 5 0043

0 5 0 0 43 0 5 0 04 3 0 5 0 043

Some of the paths never


050043 0 5 0 0 43 lead to a solution
num = “050043” 050043

50043 0043 43 3
[0] [05] [0500] [05004] [050043]

0043 3 3
[0, 5] [05, 004] [0500,4] [05004, 3]

43
[0, 500] [05,004,3] [0500,4,3]

3
[0, 500, 4]
Optimize the previous solutions using pruning

Fair Distribution of Cookies


Pair Programming

Subsets
Is that all recursion can do for us?
Divide and Conquer
Let’s start with a problem

Convert Sorted Array to Binary Search Tree


Divide and Conquer is a problem-solving technique where
a large problem is broken down into smaller sub-problems that are easier
to solve independently
Divide the problem into a number of subproblems that are smaller
instances of the same problem.

Conquer the subproblems by solving them recursively.

Combine the solutions to the subproblems into the solution for the original
problem.
If you have noticed, Tree problems are usually divide and conquer
problems

Can you mention one problem from your previous lecture?


Pair Programming

Longest Nice Substring


Things to pay attention
Incorrectly updating state variables
def backtrack(s, partial, remaining):
if is_solution(partial):
return partial

for c in remaining:
# Error: modifying iterable while iterating
remaining.remove(c)
backtrack(s, partial+c, remaining)
remaining.append(c) Cannot edit dictionary
while iterating
Shallow copying a list
def permutation():
if len(path) == len(nums):
Since the path is passed
answer.append(path)
by reference the final
return permutation list is
going to be a list of the
same path
for num in nums:
if num in path:
continue
path.append(num)
permutation()
path.pop()
Practice Questions
Backtracking Divide and Conquer
Combinations
Majority Element
Permutation

Subsets
Maximum Binary Tree

Subsets II Sort List


Combination Sum
Balance a binary search tree
Splitting a string into descending consecutive values

Additive Numbers

Sudoku Solver

N-Queens

N-Queens II

You might also like