Skip to content

Commit

Permalink
[chore] add markdown version of recursion notebooks
Browse files Browse the repository at this point in the history
  • Loading branch information
gitgik committed Feb 25, 2021
1 parent 09711dd commit 571e9b5
Show file tree
Hide file tree
Showing 9 changed files with 541 additions and 0 deletions.
71 changes: 71 additions & 0 deletions recursion/bst_diameter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
### Problem
Given the root of a Binary Search Tree, find its diameter.

Sample input:

```
10
/ \
5 15
/ \ \
2 5 22
/
1
```

Expected output:

```
6 => (We get 6 from counting 1, 2, 5, 10, 15, 22)
```

### Solution
The diameter of a tree T is the largest (max) of the following quantities:

- the diameter of T’s left subtree.
- the diameter of T’s right subtree.
- the longest path between leaves that goes through the root of T (this can be computed from the heights of the subtrees of T)


```python
class Node:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right

def height(root):
if root is None:
return 0
return 1 + max(height(root.left), height(root.right))

def diameter(root):
# base case: tree is empty
if root is None:
return 0

lheight = height(root.left)
rheight = height(root.right)

ldiameter = diameter(root.left)
rdiameter = diameter(root.right)

return max(lheight + rheight + 1, max(ldiameter, rdiameter))
```


```python
bst = Node(6, Node(3, Node(1), Node(4, Node(2))), Node(8, Node(7), Node(9)))
```


```python
diameter(bst)
```




6


45 changes: 45 additions & 0 deletions recursion/count_sets_adding_to_target.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
## Problem
Given an array of non-negative integers, find the number of all the sets that add up to a given target.

Sample input:
```
[1, 2, 4, 5, 7], Target = 7
```

The output is `3`, i.e {1, 2, 4}, {2, 5} and {7} all add up to 7


```python
def count_sets(arr, total):
return helper(arr, total, len(arr) - 1, {})

def helper(arr, total, i, mem) -> int:
key = f"{total}:{i}"
if key in mem:
return mem[key]

if total == 0:
return 1
elif total < 0:
return 0
elif i < 0:
return 0
elif total < arr[i]:
to_return = helper(arr, total, i - 1, mem)
else:
to_return = helper(arr, total - arr[i], i - 1, mem) + helper(arr, total, i - 1, mem)
mem[key] = to_return
return to_return
```


```python
count_sets([1, 2, 4, 5, 7], 7)
```




3


51 changes: 51 additions & 0 deletions recursion/fibonacci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
```python
def fibonacci(n, memoize={1:0, 2: 1}):
"""
Nth fibonacci number using memoization.
O(n) space and time
"""
if n < 0:
raise TypeError('Value of n must be a positive integer')
if n in memoize:
return memoize[n]
else:
memoize[n] = fibonacci(n - 1, memoize) + fibonacci(n - 2, memoize)
return memoize[n]


def efficient_fibonacci(n):
"""
nth fibonacci number iteratively.
Complexity:
O(n) time,
O(1) space, since we are only storing two array values at any given time.
"""
last_two = [0, 1]

counter = 3
while counter <= n:
next_fibonacci = sum(last_two)
last_two = [last_two[1], next_fibonacci]
counter += 1
# the else clause caters for the edge case of the first fibo number == 0
return last_two[1] if n > 1 else last_two[0]
```


```python
efficient_fibonacci(12)
```




89




```python

```
87 changes: 87 additions & 0 deletions recursion/greatest_common_divisor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
## Problem
Given n numbers, find the greatest common denominator between them.

For example, given the numbers [42, 56, 14], return 14

## Solution
Because the greatest common divisor is associative, gcd of multiple numbers: say `a, b, c` is equivalent to
```
gcd(gcd(ab),c)
```
By definition, if the divisor divides gcd(a, b) and c, it must divide a and b as well.

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.**


```python
def gcd(numbers):
n = numbers[0]
for num in numbers[1:]:
n = _gcd(n, num)
return n
```

A naive implementation might try every integer from 1 to min(a, b) and see of it divides the larger:


```python
def _gcd_naive(a, b):
# find smallest value, largest value of the two
smaller, larger = min(a, b), max(a,b)
# iteratively check every integer from the smallest value to 1.
for divisor in range(smaller, 0, -1):
if larger % divisor == 0:
return divisor
```

A more efficient method is the Euclidean algorithm which follows a recursive formula:

```
gcd(a, 0) = a
gcd(a, b) = gcd(b, a % b)
```


```python
def _gcd(a, b):
if b == 0:
return a
return _gcd(b, a % b)
```


```python
# time to test it out
gcd([42, 56, 14, 7])
```




7



Here's a more memory-efficient method that works bottom up:


```python
def _gcd(a, b):
while b:
a, b = b, a % b
return a

# test it out
gcd([0, 64, 256, 128])
```




64



For more details check out this video tutorial on how [Euclidean algorithm](https://www.youtube.com/watch?v=p5gn2hj51hs) works.


69 changes: 69 additions & 0 deletions recursion/matrix_maximum_coins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
### Problem
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.

For example, in this matrix

```python
0 3 1 1
2 0 0 4
1 5 3 1
```

The most we can collect is 0 + 2 + 1 + 5 + 3 + 1 = 12 coins.

### Approach
Consider the following matrix,

```
0 2 3
5 4 9
6 7 1
```

- If we are at 1, the most coins we can collect is ` 1 `
- If we start at 7, the most coins we'll co is `7 + 1`
- If we start at 9, the most coins we can have is `9 + 1`
- If we start at 4, the most coins we can collect is `max(7 + 1, 9 + 1)`

This is a recursive problem.

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.
This leads to the following solution:


```python
def collect_coins(matrix, r=0, c=0, cache=None):
if cache is None:
cache = {}

is_bottom = (r == len(matrix) - 1)
is_rightmost = (c == len(matrix[0]) - 1)

if (r, c) not in cache:
if is_bottom and is_rightmost:
cache[r, c] = matrix[r][c]
elif is_bottom:
cache[r, c] = matrix[r][c] + collect_coins(matrix, r, c + 1, cache)
elif is_rightmost:
cache[r, c] = matrix[r][c] + collect_coins(matrix, r + 1, c, cache)
else:
cache[r, c] = matrix[r][c] + max(collect_coins(matrix, r, c + 1, cache), collect_coins(matrix, r + 1, c, cache))
return cache[r, c]
```


```python
matrix = [
[0, 3, 1, 1],
[2, 0, 0, 4],
[1, 5, 3, 1]
]
collect_coins(matrix)
```




12


51 changes: 51 additions & 0 deletions recursion/min_max.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
### Problem
Given an array of numbers of length N, find both the minimum and maximum using less than 2 * (N - 2) comparisons.


```python
def min_max(array):
if len(array) == 1:
return array[0], array[0]
elif len(array) == 2:
return (array[0], array[1]) if array[0] < array[1] else (array[1], array[0])
else:
mid = len(array) // 2
left_min, left_max = min_max(array[:mid])
right_min, right_max = min_max(array[mid:])
return min(left_min, right_min), max(left_max, right_max)

```


```python
min_max([1, 2, 3, 4])
```




(1, 4)



For array of size N, we are breaking down the problem into two subproblems of size `N / 2` plus `2` comparisons.

```python
# Recursively break down array
[4, 2, 7, 5, -1, 3, 6]
[[4, 2, 7, 5], [-1, 3, 6]]
[[[4, 2], [7, 5]], [[-1, 3], [6]]]

# Base case: reorder so that smaller comes before larger
[[[2, 4], [5, 7]], [[-1, 3], [6, 6]]]

# Merge to find min and max
[[min(2, 5), max(4, 7)], [min(-1, 6), max(3, 6)]]
[min(2, -1), max(7, 6)]
[-1, 7]
```


```python

```
Loading

0 comments on commit 571e9b5

Please sign in to comment.