A recursive function is defined in terms of base cases and recursive steps.
- In a base case, we compute the result immediately given the inputs to the function call.
- In a recursive step, we compute the result with the help of one or more recursive calls to this same function, but with the inputs somehow reduced in size or complexity, closer to a base case.
To calculate Factorial of n:
n! = n x (n-1) x ... x 2 x 1
Iterative Implementation
def factorial(n: int) -> int:
fact = 1
for i in range(n):
fact = fact * i
}
return factRecurrence relation
n! = {
1 if n = 0
(n-1)! x n if n > 0
}
Recursive Implementation
def factorial(n: int) -> int:
if n = 0:
return 1
return n * factorial(n-1)- Divide the problem into a number of subproblems that are smaller instances of the same problem.
- Conquer the subproblems by solving them recursively. If they are small enough, solve the subproblems as base cases.
- Combine the solutions to the subproblems into the solution for the original problem.
def divide_conquer(problem, param1, param2, ...):
# recursion terminator
if problem is None:
process_result
return
# prepare data (key is to how to split the problem)
data = prepare_data(problem)
subproblems = split_problem(problem, data)
# conquer subproblems
subresult1 = self.divide_conquer(subproblems[0], p1, ...)
subresult2 = self.divide_conquer(subproblems[1], p1, ...)
subresult3 = self.divide_conquer(subproblems[2], p1, ...)
...
# process and generate the final result
result = process_result(subresult1, subresult2, subresult3, ...)
# revert the current level states- Sometimes we need to return multiple results (tuple)
- Sometimes we need global variables to easily update the final result
Leetcode Problems
- 21. Merge Two Sorted Lists
- 206. Reverse Linked List
- 105. Construct Binary Tree from Preorder and Inorder Traversal
- 236. Lowest Common Ancestor of a Binary Tree
- 169. Majority Element
Backtracking can be defined as a general algorithmic technique that considers searching every possible combination in order to solve a computational problem.
result = []
def backtrack(path):
if end condition:
result.add(path[:]) # param pass by reference
return
if some condition: # Pruning if necessary
return
# Get the choice list
for choice in choices:
# get rid of the illegal choices
if exclusive condition:
continue
path.append(choice) # Make the choice
backtrack(path, choices) # enter the next decision tree
path.pop() # Remove the choice (since it's already made)- Time complexity for backtrack algorithm is at least O(N!)
- Backtrack is a decision tree, updating the result is actually a preorder and/or postorder recursion (DFS)
- Sometimes we don't need to explicitly maintain the choice list, we derive it using other parameters (e.g. index)
- Sometimes path can be a string instead of an array, and we use
path += 'choice'andpath = path[:-1]to make and remove choice
Leetcode Problems