Skip to content

Commit 571e9b5

Browse files
committed
[chore] add markdown version of recursion notebooks
1 parent 09711dd commit 571e9b5

9 files changed

+541
-0
lines changed

recursion/bst_diameter.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
### Problem
2+
Given the root of a Binary Search Tree, find its diameter.
3+
4+
Sample input:
5+
6+
```
7+
10
8+
/ \
9+
5 15
10+
/ \ \
11+
2 5 22
12+
/
13+
1
14+
```
15+
16+
Expected output:
17+
18+
```
19+
6 => (We get 6 from counting 1, 2, 5, 10, 15, 22)
20+
```
21+
22+
### Solution
23+
The diameter of a tree T is the largest (max) of the following quantities:
24+
25+
- the diameter of T’s left subtree.
26+
- the diameter of T’s right subtree.
27+
- the longest path between leaves that goes through the root of T (this can be computed from the heights of the subtrees of T)
28+
29+
30+
```python
31+
class Node:
32+
def __init__(self, value, left=None, right=None):
33+
self.value = value
34+
self.left = left
35+
self.right = right
36+
37+
def height(root):
38+
if root is None:
39+
return 0
40+
return 1 + max(height(root.left), height(root.right))
41+
42+
def diameter(root):
43+
# base case: tree is empty
44+
if root is None:
45+
return 0
46+
47+
lheight = height(root.left)
48+
rheight = height(root.right)
49+
50+
ldiameter = diameter(root.left)
51+
rdiameter = diameter(root.right)
52+
53+
return max(lheight + rheight + 1, max(ldiameter, rdiameter))
54+
```
55+
56+
57+
```python
58+
bst = Node(6, Node(3, Node(1), Node(4, Node(2))), Node(8, Node(7), Node(9)))
59+
```
60+
61+
62+
```python
63+
diameter(bst)
64+
```
65+
66+
67+
68+
69+
6
70+
71+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
## Problem
2+
Given an array of non-negative integers, find the number of all the sets that add up to a given target.
3+
4+
Sample input:
5+
```
6+
[1, 2, 4, 5, 7], Target = 7
7+
```
8+
9+
The output is `3`, i.e {1, 2, 4}, {2, 5} and {7} all add up to 7
10+
11+
12+
```python
13+
def count_sets(arr, total):
14+
return helper(arr, total, len(arr) - 1, {})
15+
16+
def helper(arr, total, i, mem) -> int:
17+
key = f"{total}:{i}"
18+
if key in mem:
19+
return mem[key]
20+
21+
if total == 0:
22+
return 1
23+
elif total < 0:
24+
return 0
25+
elif i < 0:
26+
return 0
27+
elif total < arr[i]:
28+
to_return = helper(arr, total, i - 1, mem)
29+
else:
30+
to_return = helper(arr, total - arr[i], i - 1, mem) + helper(arr, total, i - 1, mem)
31+
mem[key] = to_return
32+
return to_return
33+
```
34+
35+
36+
```python
37+
count_sets([1, 2, 4, 5, 7], 7)
38+
```
39+
40+
41+
42+
43+
3
44+
45+

recursion/fibonacci.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
```python
2+
def fibonacci(n, memoize={1:0, 2: 1}):
3+
"""
4+
Nth fibonacci number using memoization.
5+
6+
O(n) space and time
7+
"""
8+
if n < 0:
9+
raise TypeError('Value of n must be a positive integer')
10+
if n in memoize:
11+
return memoize[n]
12+
else:
13+
memoize[n] = fibonacci(n - 1, memoize) + fibonacci(n - 2, memoize)
14+
return memoize[n]
15+
16+
17+
def efficient_fibonacci(n):
18+
"""
19+
nth fibonacci number iteratively.
20+
21+
Complexity:
22+
O(n) time,
23+
O(1) space, since we are only storing two array values at any given time.
24+
"""
25+
last_two = [0, 1]
26+
27+
counter = 3
28+
while counter <= n:
29+
next_fibonacci = sum(last_two)
30+
last_two = [last_two[1], next_fibonacci]
31+
counter += 1
32+
# the else clause caters for the edge case of the first fibo number == 0
33+
return last_two[1] if n > 1 else last_two[0]
34+
```
35+
36+
37+
```python
38+
efficient_fibonacci(12)
39+
```
40+
41+
42+
43+
44+
89
45+
46+
47+
48+
49+
```python
50+
51+
```

recursion/greatest_common_divisor.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
## Problem
2+
Given n numbers, find the greatest common denominator between them.
3+
4+
For example, given the numbers [42, 56, 14], return 14
5+
6+
## Solution
7+
Because the greatest common divisor is associative, gcd of multiple numbers: say `a, b, c` is equivalent to
8+
```
9+
gcd(gcd(ab),c)
10+
```
11+
By definition, if the divisor divides gcd(a, b) and c, it must divide a and b as well.
12+
13+
The GCD of multiple numbers `a, b, ... n` can be obtained by **iteratively computing the GCD of `a and b`, and GCD of the result of that with `c` and so on.**
14+
15+
16+
```python
17+
def gcd(numbers):
18+
n = numbers[0]
19+
for num in numbers[1:]:
20+
n = _gcd(n, num)
21+
return n
22+
```
23+
24+
A naive implementation might try every integer from 1 to min(a, b) and see of it divides the larger:
25+
26+
27+
```python
28+
def _gcd_naive(a, b):
29+
# find smallest value, largest value of the two
30+
smaller, larger = min(a, b), max(a,b)
31+
# iteratively check every integer from the smallest value to 1.
32+
for divisor in range(smaller, 0, -1):
33+
if larger % divisor == 0:
34+
return divisor
35+
```
36+
37+
A more efficient method is the Euclidean algorithm which follows a recursive formula:
38+
39+
```
40+
gcd(a, 0) = a
41+
gcd(a, b) = gcd(b, a % b)
42+
```
43+
44+
45+
```python
46+
def _gcd(a, b):
47+
if b == 0:
48+
return a
49+
return _gcd(b, a % b)
50+
```
51+
52+
53+
```python
54+
# time to test it out
55+
gcd([42, 56, 14, 7])
56+
```
57+
58+
59+
60+
61+
7
62+
63+
64+
65+
Here's a more memory-efficient method that works bottom up:
66+
67+
68+
```python
69+
def _gcd(a, b):
70+
while b:
71+
a, b = b, a % b
72+
return a
73+
74+
# test it out
75+
gcd([0, 64, 256, 128])
76+
```
77+
78+
79+
80+
81+
64
82+
83+
84+
85+
For more details check out this video tutorial on how [Euclidean algorithm](https://www.youtube.com/watch?v=p5gn2hj51hs) works.
86+
87+

recursion/matrix_maximum_coins.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
### Problem
2+
You are given a 2-d matrix where each cell represents number of coins in that cell. Assuming we start at matrix[0][0], and can only move right or down, find the maximum number of coins you can collect by the bottom right corner.
3+
4+
For example, in this matrix
5+
6+
```python
7+
0 3 1 1
8+
2 0 0 4
9+
1 5 3 1
10+
```
11+
12+
The most we can collect is 0 + 2 + 1 + 5 + 3 + 1 = 12 coins.
13+
14+
### Approach
15+
Consider the following matrix,
16+
17+
```
18+
0 2 3
19+
5 4 9
20+
6 7 1
21+
```
22+
23+
- If we are at 1, the most coins we can collect is ` 1 `
24+
- If we start at 7, the most coins we'll co is `7 + 1`
25+
- If we start at 9, the most coins we can have is `9 + 1`
26+
- If we start at 4, the most coins we can collect is `max(7 + 1, 9 + 1)`
27+
28+
This is a recursive problem.
29+
30+
The base case will be when we get to the bottom-right corner of the matrix – the most amount of coins we can collect at that point is that cell.
31+
This leads to the following solution:
32+
33+
34+
```python
35+
def collect_coins(matrix, r=0, c=0, cache=None):
36+
if cache is None:
37+
cache = {}
38+
39+
is_bottom = (r == len(matrix) - 1)
40+
is_rightmost = (c == len(matrix[0]) - 1)
41+
42+
if (r, c) not in cache:
43+
if is_bottom and is_rightmost:
44+
cache[r, c] = matrix[r][c]
45+
elif is_bottom:
46+
cache[r, c] = matrix[r][c] + collect_coins(matrix, r, c + 1, cache)
47+
elif is_rightmost:
48+
cache[r, c] = matrix[r][c] + collect_coins(matrix, r + 1, c, cache)
49+
else:
50+
cache[r, c] = matrix[r][c] + max(collect_coins(matrix, r, c + 1, cache), collect_coins(matrix, r + 1, c, cache))
51+
return cache[r, c]
52+
```
53+
54+
55+
```python
56+
matrix = [
57+
[0, 3, 1, 1],
58+
[2, 0, 0, 4],
59+
[1, 5, 3, 1]
60+
]
61+
collect_coins(matrix)
62+
```
63+
64+
65+
66+
67+
12
68+
69+

recursion/min_max.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
### Problem
2+
Given an array of numbers of length N, find both the minimum and maximum using less than 2 * (N - 2) comparisons.
3+
4+
5+
```python
6+
def min_max(array):
7+
if len(array) == 1:
8+
return array[0], array[0]
9+
elif len(array) == 2:
10+
return (array[0], array[1]) if array[0] < array[1] else (array[1], array[0])
11+
else:
12+
mid = len(array) // 2
13+
left_min, left_max = min_max(array[:mid])
14+
right_min, right_max = min_max(array[mid:])
15+
return min(left_min, right_min), max(left_max, right_max)
16+
17+
```
18+
19+
20+
```python
21+
min_max([1, 2, 3, 4])
22+
```
23+
24+
25+
26+
27+
(1, 4)
28+
29+
30+
31+
For array of size N, we are breaking down the problem into two subproblems of size `N / 2` plus `2` comparisons.
32+
33+
```python
34+
# Recursively break down array
35+
[4, 2, 7, 5, -1, 3, 6]
36+
[[4, 2, 7, 5], [-1, 3, 6]]
37+
[[[4, 2], [7, 5]], [[-1, 3], [6]]]
38+
39+
# Base case: reorder so that smaller comes before larger
40+
[[[2, 4], [5, 7]], [[-1, 3], [6, 6]]]
41+
42+
# Merge to find min and max
43+
[[min(2, 5), max(4, 7)], [min(-1, 6), max(3, 6)]]
44+
[min(2, -1), max(7, 6)]
45+
[-1, 7]
46+
```
47+
48+
49+
```python
50+
51+
```

0 commit comments

Comments
 (0)