Recursion II - No Code
Recursion II - No Code
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
def backtrack(candidate):
if candidate is a solution:
# process or output a candidate
return
# handle basecases
# 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=[]
i=0,
com=[]
insert no insert
i=1, i=1,
com=[1] com=[]
i=0,
com=[]
insert no insert
i=1, i=1,
com=[1] 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
O (n*s)
What type of backtracking was
the previous problem?
Splitting a String into Descending Consecutive values
num = “050043” 050043
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.
put at put at 1
0
i=1, i=1,
bucket=[1,0] bucket=[0,1]
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
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
= 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
Combinations
Optimize the previous solutions using pruning
0 5 0 0 43 0 5 0 04 3 0 5 0 043
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
Subsets
Is that all recursion can do for us?
Divide and Conquer
Let’s start with a problem
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
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
Additive Numbers
Sudoku Solver
N-Queens
N-Queens II