1611712081

In a previous episode of Learn with Dr G, Emilia Lazer-Walker introduced the Flight Simulator and April Speight introduced the Mixed Reality Toolkit.

In this episode join Dr G as she explores these toolkits and plays around with game development and mixed reality. This stream is casual and will include live coding and demos!

#game-development #programming #developer

1644477426

Minimal and clean example implementations of data structures and algorithms in Python 3.

For running all tests write down:

```
$ python3 -m unittest discover tests
```

For running some specific tests you can do this as following (Ex: sort):

```
$ python3 -m unittest tests.test_sort
```

For running all tests write down:

```
$ python3 -m pytest tests
```

If you want to use the API algorithms in your code, it is as simple as:

```
$ pip3 install algorithms
```

You can test by creating a python file: (Ex: use `merge_sort`

in `sort`

)

```
from algorithms.sort import merge_sort
if __name__ == "__main__":
my_list = [1, 8, 3, 5, 6]
my_list = merge_sort(my_list)
print(my_list)
```

If you want to uninstall algorithms, it is as simple as:

```
$ pip3 uninstall -y algorithms
```

- arrays
- delete_nth

```
"""
Given a list lst and a number N, create a new list
that contains each number of the list at most N times without reordering.
For example if N = 2, and the input is [1,2,3,1,2,1,2,3], you take [1,2,3,1,2],
drop the next [1,2] since this would lead to 1 and 2 being in the result 3 times, and then take 3,
which leads to [1,2,3,1,2,3]
"""
import collections
# Time complexity O(n^2)
def delete_nth_naive(array, n):
ans = []
for num in array:
if ans.count(num) < n:
ans.append(num)
return ans
# Time Complexity O(n), using hash tables.
def delete_nth(array, n):
result = []
counts = collections.defaultdict(int) # keep track of occurrences
for i in array:
if counts[i] < n:
result.append(i)
counts[i] += 1
return result
```

- flatten

```
"""
Implement Flatten Arrays.
Given an array that may contain nested arrays,
produce a single resultant array.
"""
from collections.abc import Iterable
# return list
def flatten(input_arr, output_arr=None):
if output_arr is None:
output_arr = []
for ele in input_arr:
if not isinstance(ele, str) and isinstance(ele, Iterable):
flatten(ele, output_arr) #tail-recursion
else:
output_arr.append(ele) #produce the result
return output_arr
# returns iterator
def flatten_iter(iterable):
"""
Takes as input multi dimensional iterable and
returns generator which produces one dimensional output.
"""
for element in iterable:
if not isinstance(element, str) and isinstance(element, Iterable):
yield from flatten_iter(element)
else:
yield element
```

- garage

```
"""
There is a parking lot with only one empty spot. Given the initial state
of the parking lot and the final state. Each step we are only allowed to
move a car
out of its place and move it into the empty spot.
The goal is to find out the least movement needed to rearrange
the parking lot from the initial state to the final state.
Say the initial state is an array:
[1, 2, 3, 0, 4],
where 1, 2, 3, 4 are different cars, and 0 is the empty spot.
And the final state is
[0, 3, 2, 1, 4].
We can swap 1 with 0 in the initial array to get [0, 2, 3, 1, 4] and so on.
Each step swap with 0 only.
Edit:
Now also prints the sequence of changes in states.
Output of this example :-
initial: [1, 2, 3, 0, 4]
final: [0, 3, 2, 1, 4]
Steps = 4
Sequence :
0 2 3 1 4
2 0 3 1 4
2 3 0 1 4
0 3 2 1 4
"""
def garage(initial, final):
initial = initial[::] # prevent changes in original 'initial'
seq = [] # list of each step in sequence
steps = 0
while initial != final:
zero = initial.index(0)
if zero != final.index(0): # if zero isn't where it should be,
car_to_move = final[zero] # what should be where zero is,
pos = initial.index(car_to_move) # and where is it?
initial[zero], initial[pos] = initial[pos], initial[zero]
else:
for i in range(len(initial)):
if initial[i] != final[i]:
initial[zero], initial[i] = initial[i], initial[zero]
break
seq.append(initial[::])
steps += 1
return steps, seq
# e.g.: 4, [{0, 2, 3, 1, 4}, {2, 0, 3, 1, 4},
# {2, 3, 0, 1, 4}, {0, 3, 2, 1, 4}]
"""
thus:
1 2 3 0 4 -- zero = 3, true, car_to_move = final[3] = 1,
pos = initial.index(1) = 0, switched [0], [3]
0 2 3 1 4 -- zero = 0, f, initial[1] != final[1], switched 0,1
2 0 3 1 4 -- zero = 1, t, car_to_move = final[1] = 3,
pos = initial.index(3) = 2, switched [1], [2]
2 3 0 1 4 -- zero = 2, t, car_to_move = final[2] = 2,
pos = initial.index(2) = 0, switched [0], [2]
0 3 2 1 4 -- initial == final
"""
```

- josephus_problem

```
"""
There are people sitting in a circular fashion,
print every third member while removing them,
the next counter starts immediately after the member is removed.
Print till all the members are exhausted.
For example:
Input: consider 123456789 members sitting in a circular fashion,
Output: 369485271
"""
def josephus(int_list, skip):
skip = skip - 1 # list starts with 0 index
idx = 0
len_list = (len(int_list))
while len_list > 0:
idx = (skip + idx) % len_list # hash index to every 3rd
yield int_list.pop(idx)
len_list -= 1
```

- limit

```
"""
Sometimes you need to limit array result to use. Such as you only need the
value over 10 or, you need value under than 100. By use this algorithms, you
can limit your array to specific value
If array, Min, Max value was given, it returns array that contains values of
given array which was larger than Min, and lower than Max. You need to give
'unlimit' to use only Min or Max.
ex) limit([1,2,3,4,5], None, 3) = [1,2,3]
Complexity = O(n)
"""
# tl:dr -- array slicing by value
def limit(arr, min_lim=None, max_lim=None):
return list(filter(lambda x: (min_lim <= x <= max_lim), arr))
```

- longest_non_repeat

```
"""
Given a string, find the length of the longest substring
without repeating characters.
Examples:
Given "abcabcbb", the answer is "abc", which the length is 3.
Given "bbbbb", the answer is "b", with the length of 1.
Given "pwwkew", the answer is "wke", with the length of 3.
Note that the answer must be a substring,
"pwke" is a subsequence and not a substring.
"""
def longest_non_repeat_v1(string):
"""
Find the length of the longest substring
without repeating characters.
"""
if string is None:
return 0
dict = {}
max_length = 0
j = 0
for i in range(len(string)):
if string[i] in dict:
j = max(dict[string[i]], j)
dict[string[i]] = i + 1
max_length = max(max_length, i - j + 1)
return max_length
def longest_non_repeat_v2(string):
"""
Find the length of the longest substring
without repeating characters.
Uses alternative algorithm.
"""
if string is None:
return 0
start, max_len = 0, 0
used_char = {}
for index, char in enumerate(string):
if char in used_char and start <= used_char[char]:
start = used_char[char] + 1
else:
max_len = max(max_len, index - start + 1)
used_char[char] = index
return max_len
# get functions of above, returning the max_len and substring
def get_longest_non_repeat_v1(string):
"""
Find the length of the longest substring
without repeating characters.
Return max_len and the substring as a tuple
"""
if string is None:
return 0, ''
sub_string = ''
dict = {}
max_length = 0
j = 0
for i in range(len(string)):
if string[i] in dict:
j = max(dict[string[i]], j)
dict[string[i]] = i + 1
if i - j + 1 > max_length:
max_length = i - j + 1
sub_string = string[j: i + 1]
return max_length, sub_string
def get_longest_non_repeat_v2(string):
"""
Find the length of the longest substring
without repeating characters.
Uses alternative algorithm.
Return max_len and the substring as a tuple
"""
if string is None:
return 0, ''
sub_string = ''
start, max_len = 0, 0
used_char = {}
for index, char in enumerate(string):
if char in used_char and start <= used_char[char]:
start = used_char[char] + 1
else:
if index - start + 1 > max_len:
max_len = index - start + 1
sub_string = string[start: index + 1]
used_char[char] = index
return max_len, sub_string
```

- max_ones_index

```
"""
Find the index of 0 to be replaced with 1 to get
longest continuous sequence
of 1s in a binary array.
Returns index of 0 to be
replaced with 1 to get longest
continuous sequence of 1s.
If there is no 0 in array, then
it returns -1.
e.g.
let input array = [1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1]
If we replace 0 at index 3 with 1, we get the longest continuous
sequence of 1s in the array.
So the function return => 3
"""
def max_ones_index(arr):
n = len(arr)
max_count = 0
max_index = 0
prev_zero = -1
prev_prev_zero = -1
for curr in range(n):
# If current element is 0,
# then calculate the difference
# between curr and prev_prev_zero
if arr[curr] == 0:
if curr - prev_prev_zero > max_count:
max_count = curr - prev_prev_zero
max_index = prev_zero
prev_prev_zero = prev_zero
prev_zero = curr
if n - prev_prev_zero > max_count:
max_index = prev_zero
return max_index
```

- merge_intervals

```
"""
In mathematics, a (real) interval is a set of real
numbers with the property that any number that lies
between two numbers in the set is also included in the set.
"""
class Interval:
"""
A set of real numbers with methods to determine if other
numbers are included in the set.
Includes related methods to merge and print interval sets.
"""
def __init__(self, start=0, end=0):
self.start = start
self.end = end
def __repr__(self):
return "Interval ({}, {})".format(self.start, self.end)
def __iter__(self):
return iter(range(self.start, self.end))
def __getitem__(self, index):
if index < 0:
return self.end + index
return self.start + index
def __len__(self):
return self.end - self.start
def __contains__(self, item):
if self.start >= item >= self.end:
return True
return False
def __eq__(self, other):
if self.start == other.start and self.end == other.end:
return True
return False
def as_list(self):
""" Return interval as list. """
return list(self)
@staticmethod
def merge(intervals):
""" Merge two intervals into one. """
out = []
for i in sorted(intervals, key=lambda i: i.start):
if out and i.start <= out[-1].end:
out[-1].end = max(out[-1].end, i.end)
else:
out += i,
return out
@staticmethod
def print_intervals(intervals):
""" Print out the intervals. """
res = []
for i in intervals:
res.append(repr(i))
print("".join(res))
def merge_intervals(intervals):
""" Merge intervals in the form of a list. """
if intervals is None:
return None
intervals.sort(key=lambda i: i[0])
out = [intervals.pop(0)]
for i in intervals:
if out[-1][-1] >= i[0]:
out[-1][-1] = max(out[-1][-1], i[-1])
else:
out.append(i)
return out
```

- missing_ranges

```
"""
Find missing ranges between low and high in the given array.
Ex) [3, 5] lo=1 hi=10 => answer: [(1, 2), (4, 4), (6, 10)]
"""
def missing_ranges(arr, lo, hi):
res = []
start = lo
for n in arr:
if n == start:
start += 1
elif n > start:
res.append((start, n-1))
start = n + 1
if start <= hi: # after done iterating thru array,
res.append((start, hi)) # append remainder to list
return res
```

- plus_one

```
"""
Given a non-negative number represented as an array of digits,
adding one to each numeral.
The digits are stored big-endian, such that the most significant
digit is at the head of the list.
"""
def plus_one_v1(digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
digits[-1] = digits[-1] + 1
res = []
ten = 0
i = len(digits)-1
while i >= 0 or ten == 1:
summ = 0
if i >= 0:
summ += digits[i]
if ten:
summ += 1
res.append(summ % 10)
ten = summ // 10
i -= 1
return res[::-1]
def plus_one_v2(digits):
n = len(digits)
for i in range(n-1, -1, -1):
if digits[i] < 9:
digits[i] += 1
return digits
digits[i] = 0
digits.insert(0, 1)
return digits
def plus_one_v3(num_arr):
for idx in reversed(list(enumerate(num_arr))):
num_arr[idx[0]] = (num_arr[idx[0]] + 1) % 10
if num_arr[idx[0]]:
return num_arr
return [1] + num_arr
```

- rotate

```
"""
Rotate an array of n elements to the right by k steps.
For example, with n = 7 and k = 3,
the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].
Note:
Try to come up as many solutions as you can,
there are at least 3 different ways to solve this problem.
"""
def rotate_v1(array, k):
"""
Rotate the entire array 'k' times
T(n)- O(nk)
:type array: List[int]
:type k: int
:rtype: void Do not return anything, modify array in-place instead.
"""
array = array[:]
n = len(array)
for i in range(k): # unused variable is not a problem
temp = array[n - 1]
for j in range(n-1, 0, -1):
array[j] = array[j - 1]
array[0] = temp
return array
def rotate_v2(array, k):
"""
Reverse segments of the array, followed by the entire array
T(n)- O(n)
:type array: List[int]
:type k: int
:rtype: void Do not return anything, modify nums in-place instead.
"""
array = array[:]
def reverse(arr, a, b):
while a < b:
arr[a], arr[b] = arr[b], arr[a]
a += 1
b -= 1
n = len(array)
k = k % n
reverse(array, 0, n - k - 1)
reverse(array, n - k, n - 1)
reverse(array, 0, n - 1)
return array
def rotate_v3(array, k):
if array is None:
return None
length = len(array)
k = k % length
return array[length - k:] + array[:length - k]
```

- summarize_ranges

```
"""
Given a sorted integer array without duplicates,
return the summary of its ranges.
For example, given [0, 1, 2, 4, 5, 7], return [(0, 2), (4, 5), (7, 7)].
"""
def summarize_ranges(array):
"""
:type array: List[int]
:rtype: List[]
"""
res = []
if len(array) == 1:
return [str(array[0])]
i = 0
while i < len(array):
num = array[i]
while i + 1 < len(array) and array[i + 1] - array[i] == 1:
i += 1
if array[i] != num:
res.append((num, array[i]))
else:
res.append((num, num))
i += 1
return res
```

- three_sum

```
"""
Given an array S of n integers, are there three distinct elements
a, b, c in S such that a + b + c = 0?
Find all unique triplets in the array which gives the sum of zero.
Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
{
(-1, 0, 1),
(-1, -1, 2)
}
"""
def three_sum(array):
"""
:param array: List[int]
:return: Set[ Tuple[int, int, int] ]
"""
res = set()
array.sort()
for i in range(len(array) - 2):
if i > 0 and array[i] == array[i - 1]:
continue
l, r = i + 1, len(array) - 1
while l < r:
s = array[i] + array[l] + array[r]
if s > 0:
r -= 1
elif s < 0:
l += 1
else:
# found three sum
res.add((array[i], array[l], array[r]))
# remove duplicates
while l < r and array[l] == array[l + 1]:
l += 1
while l < r and array[r] == array[r - 1]:
r -= 1
l += 1
r -= 1
return res
```

- trimmean

```
"""
When make reliable means, we need to neglect best and worst values.
For example, when making average score on athletes we need this option.
So, this algorithm affixes some percentage to neglect when making mean.
For example, if you suggest 20%, it will neglect the best 10% of values
and the worst 10% of values.
This algorithm takes an array and percentage to neglect. After sorted,
if index of array is larger or smaller than desired ratio, we don't
compute it.
Compleity: O(n)
"""
def trimmean(arr, per):
ratio = per/200
# /100 for easy calculation by *, and /2 for easy adaption to best and worst parts.
cal_sum = 0
# sum value to be calculated to trimmean.
arr.sort()
neg_val = int(len(arr)*ratio)
arr = arr[neg_val:len(arr)-neg_val]
for i in arr:
cal_sum += i
return cal_sum/len(arr)
```

- top_1

```
"""
This algorithm receives an array and returns most_frequent_value
Also, sometimes it is possible to have multiple 'most_frequent_value's,
so this function returns a list. This result can be used to find a
representative value in an array.
This algorithm gets an array, makes a dictionary of it,
finds the most frequent count, and makes the result list.
For example: top_1([1, 1, 2, 2, 3, 4]) will return [1, 2]
(TL:DR) Get mathematical Mode
Complexity: O(n)
"""
def top_1(arr):
values = {}
#reserve each value which first appears on keys
#reserve how many time each value appears by index number on values
result = []
f_val = 0
for i in arr:
if i in values:
values[i] += 1
else:
values[i] = 1
f_val = max(values.values())
for i in values.keys():
if values[i] == f_val:
result.append(i)
else:
continue
return result
```

- two_sum

```
"""
Given an array of integers, return indices of the two numbers
such that they add up to a specific target.
You may assume that each input would have exactly one solution,
and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return (0, 1)
"""
def two_sum(array, target):
dic = {}
for i, num in enumerate(array):
if num in dic:
return dic[num], i
else:
dic[target - num] = i
return None
```

- move_zeros

```
"""
Write an algorithm that takes an array and moves all of the zeros to the end,
preserving the order of the other elements.
move_zeros([false, 1, 0, 1, 2, 0, 1, 3, "a"])
returns => [false, 1, 1, 2, 1, 3, "a", 0, 0]
The time complexity of the below algorithm is O(n).
"""
# False == 0 is True
def move_zeros(array):
result = []
zeros = 0
for i in array:
if i == 0 and type(i) != bool: # not using `not i` to avoid `False`, `[]`, etc.
zeros += 1
else:
result.append(i)
result.extend([0] * zeros)
return result
print(move_zeros([False, 1, 0, 1, 2, 0, 1, 3, "a"]))
```

- n_sum

```
"""
Given an array of n integers, are there elements a, b, .. , n in nums
such that a + b + .. + n = target?
Find all unique n-tuplets in the array which gives the sum of target.
Example:
basic:
Given:
n = 4
nums = [1, 0, -1, 0, -2, 2]
target = 0,
return [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]
advanced:
Given:
n = 2
nums = [[-3, 0], [-2, 1], [2, 2], [3, 3], [8, 4], [-9, 5]]
target = -5
def sum(a, b):
return [a[0] + b[1], a[1] + b[0]]
def compare(num, target):
if num[0] < target:
return -1
elif if num[0] > target:
return 1
else:
return 0
return [[-9, 5], [8, 4]]
(TL:DR) because -9 + 4 = -5
"""
def n_sum(n, nums, target, **kv):
"""
n: int
nums: list[object]
target: object
sum_closure: function, optional
Given two elements of nums, return sum of both.
compare_closure: function, optional
Given one object of nums and target, return -1, 1, or 0.
same_closure: function, optional
Given two object of nums, return bool.
return: list[list[object]]
Note:
1. type of sum_closure's return should be same
as type of compare_closure's first param
"""
def sum_closure_default(a, b):
return a + b
def compare_closure_default(num, target):
""" above, below, or right on? """
if num < target:
return -1
elif num > target:
return 1
else:
return 0
def same_closure_default(a, b):
return a == b
def n_sum(n, nums, target):
if n == 2: # want answers with only 2 terms? easy!
results = two_sum(nums, target)
else:
results = []
prev_num = None
for index, num in enumerate(nums):
if prev_num is not None and \
same_closure(prev_num, num):
continue
prev_num = num
n_minus1_results = (
n_sum( # recursive call
n - 1, # a
nums[index + 1:], # b
target - num # c
) # x = n_sum( a, b, c )
) # n_minus1_results = x
n_minus1_results = (
append_elem_to_each_list(num, n_minus1_results)
)
results += n_minus1_results
return union(results)
def two_sum(nums, target):
nums.sort()
lt = 0
rt = len(nums) - 1
results = []
while lt < rt:
sum_ = sum_closure(nums[lt], nums[rt])
flag = compare_closure(sum_, target)
if flag == -1:
lt += 1
elif flag == 1:
rt -= 1
else:
results.append(sorted([nums[lt], nums[rt]]))
lt += 1
rt -= 1
while (lt < len(nums) and
same_closure(nums[lt - 1], nums[lt])):
lt += 1
while (0 <= rt and
same_closure(nums[rt], nums[rt + 1])):
rt -= 1
return results
def append_elem_to_each_list(elem, container):
results = []
for elems in container:
elems.append(elem)
results.append(sorted(elems))
return results
def union(duplicate_results):
results = []
if len(duplicate_results) != 0:
duplicate_results.sort()
results.append(duplicate_results[0])
for result in duplicate_results[1:]:
if results[-1] != result:
results.append(result)
return results
sum_closure = kv.get('sum_closure', sum_closure_default)
same_closure = kv.get('same_closure', same_closure_default)
compare_closure = kv.get('compare_closure', compare_closure_default)
nums.sort()
return n_sum(n, nums, target)
```

- automata
- DFA

```
def DFA(transitions, start, final, string):
num = len(string)
num_final = len(final)
cur = start
for i in range(num):
if transitions[cur][string[i]] is None:
return False
else:
cur = transitions[cur][string[i]]
for i in range(num_final):
if cur == final[i]:
return True
return False
```

- backtrack
- general_solution.md
- add_operators

```
"""
Given a string that contains only digits 0-9 and a target value,
return all possibilities to add binary operators (not unary) +, -, or *
between the digits so they prevuate to the target value.
Examples:
"123", 6 -> ["1+2+3", "1*2*3"]
"232", 8 -> ["2*3+2", "2+3*2"]
"105", 5 -> ["1*0+5","10-5"]
"00", 0 -> ["0+0", "0-0", "0*0"]
"3456237490", 9191 -> []
"""
def add_operators(num, target):
"""
:type num: str
:type target: int
:rtype: List[str]
"""
def dfs(res, path, num, target, pos, prev, multed):
if pos == len(num):
if target == prev:
res.append(path)
return
for i in range(pos, len(num)):
if i != pos and num[pos] == '0': # all digits have to be used
break
cur = int(num[pos:i+1])
if pos == 0:
dfs(res, path + str(cur), num, target, i+1, cur, cur)
else:
dfs(res, path + "+" + str(cur), num, target,
i+1, prev + cur, cur)
dfs(res, path + "-" + str(cur), num, target,
i+1, prev - cur, -cur)
dfs(res, path + "*" + str(cur), num, target,
i+1, prev - multed + multed * cur, multed * cur)
res = []
if not num:
return res
dfs(res, "", num, target, 0, 0, 0)
return res
```

- anagram

```
"""
Given two strings, determine if they are equal after reordering.
Examples:
"apple", "pleap" -> True
"apple", "cherry" -> False
"""
def anagram(s1, s2):
c1 = [0] * 26
c2 = [0] * 26
for c in s1:
pos = ord(c)-ord('a')
c1[pos] = c1[pos] + 1
for c in s2:
pos = ord(c)-ord('a')
c2[pos] = c2[pos] + 1
return c1 == c2
```

- array_sum_combinations

```
"""
WAP to take one element from each of the array add it to the target sum.
Print all those three-element combinations.
/*
A = [1, 2, 3, 3]
B = [2, 3, 3, 4]
C = [2, 3, 3, 4]
target = 7
*/
Result:
[[1, 2, 4], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 3, 3], [1, 4, 2],
[2, 2, 3], [2, 2, 3], [2, 3, 2], [2, 3, 2], [3, 2, 2], [3, 2, 2]]
"""
import itertools
from functools import partial
def array_sum_combinations(A, B, C, target):
def over(constructed_sofar):
sum = 0
to_stop, reached_target = False, False
for elem in constructed_sofar:
sum += elem
if sum >= target or len(constructed_sofar) >= 3:
to_stop = True
if sum == target and 3 == len(constructed_sofar):
reached_target = True
return to_stop, reached_target
def construct_candidates(constructed_sofar):
array = A
if 1 == len(constructed_sofar):
array = B
elif 2 == len(constructed_sofar):
array = C
return array
def backtrack(constructed_sofar=[], res=[]):
to_stop, reached_target = over(constructed_sofar)
if to_stop:
if reached_target:
res.append(constructed_sofar)
return
candidates = construct_candidates(constructed_sofar)
for candidate in candidates:
constructed_sofar.append(candidate)
backtrack(constructed_sofar[:], res)
constructed_sofar.pop()
res = []
backtrack([], res)
return res
def unique_array_sum_combinations(A, B, C, target):
"""
1. Sort all the arrays - a,b,c. - This improves average time complexity.
2. If c[i] < Sum, then look for Sum - c[i] in array a and b.
When pair found, insert c[i], a[j] & b[k] into the result list.
This can be done in O(n).
3. Keep on doing the above procedure while going through complete c array.
Complexity: O(n(m+p))
"""
def check_sum(n, *nums):
if sum(x for x in nums) == n:
return (True, nums)
else:
return (False, nums)
pro = itertools.product(A, B, C)
func = partial(check_sum, target)
sums = list(itertools.starmap(func, pro))
res = set()
for s in sums:
if s[0] is True and s[1] not in res:
res.add(s[1])
return list(res)
```

- combination_sum

```
"""
Given a set of candidate numbers (C) (without duplicates) and a target number
(T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7] and target 7,
A solution set is:
[
[7],
[2, 2, 3]
]
"""
def combination_sum(candidates, target):
def dfs(nums, target, index, path, res):
if target < 0:
return # backtracking
if target == 0:
res.append(path)
return
for i in range(index, len(nums)):
dfs(nums, target-nums[i], i, path+[nums[i]], res)
res = []
candidates.sort()
dfs(candidates, target, 0, [], res)
return res
```

- factor_combinations

```
"""
Numbers can be regarded as product of its factors. For example,
8 = 2 x 2 x 2;
= 2 x 4.
Write a function that takes an integer n
and return all possible combinations of its factors.
Note:
You may assume that n is always positive.
Factors should be greater than 1 and less than n.
Examples:
input: 1
output:
[]
input: 37
output:
[]
input: 12
output:
[
[2, 6],
[2, 2, 3],
[3, 4]
]
input: 32
output:
[
[2, 16],
[2, 2, 8],
[2, 2, 2, 4],
[2, 2, 2, 2, 2],
[2, 4, 4],
[4, 8]
]
"""
# Iterative:
def get_factors(n):
todo, combis = [(n, 2, [])], []
while todo:
n, i, combi = todo.pop()
while i * i <= n:
if n % i == 0:
combis.append(combi + [i, n//i])
todo.append((n//i, i, combi+[i]))
i += 1
return combis
# Recursive:
def recursive_get_factors(n):
def factor(n, i, combi, combis):
while i * i <= n:
if n % i == 0:
combis.append(combi + [i, n//i]),
factor(n//i, i, combi+[i], combis)
i += 1
return combis
return factor(n, 2, [], [])
```

- generate_abbreviations

```
"""
given input word, return the list of abbreviations.
ex)
word => ['word', 'wor1', 'wo1d', 'wo2', 'w1rd', 'w1r1', 'w2d', 'w3', '1ord', '1or1', '1o1d', '1o2', '2rd', '2r1', '3d', '4']
"""
def generate_abbreviations(word):
def backtrack(result, word, pos, count, cur):
if pos == len(word):
if count > 0:
cur += str(count)
result.append(cur)
return
if count > 0: # add the current word
backtrack(result, word, pos+1, 0, cur+str(count)+word[pos])
else:
backtrack(result, word, pos+1, 0, cur+word[pos])
# skip the current word
backtrack(result, word, pos+1, count+1, cur)
result = []
backtrack(result, word, 0, 0, "")
return result
```

- generate_parenthesis

```
"""
Given n pairs of parentheses, write a function to generate
all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
"""
def generate_parenthesis_v1(n):
def add_pair(res, s, left, right):
if left == 0 and right == 0:
res.append(s)
return
if right > 0:
add_pair(res, s + ")", left, right - 1)
if left > 0:
add_pair(res, s + "(", left - 1, right + 1)
res = []
add_pair(res, "", n, 0)
return res
def generate_parenthesis_v2(n):
def add_pair(res, s, left, right):
if left == 0 and right == 0:
res.append(s)
if left > 0:
add_pair(res, s + "(", left - 1, right)
if right > 0 and left < right:
add_pair(res, s + ")", left, right - 1)
res = []
add_pair(res, "", n, n)
return res
```

- letter_combination

```
"""
Given a digit string, return all possible letter
combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below:
2: "abc"
3: "def"
4: "ghi"
5: "jkl"
6: "mno"
7: "pqrs"
8: "tuv"
9: "wxyz"
Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
"""
def letter_combinations(digits):
if digits == "":
return []
kmaps = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz"
}
ans = [""]
for num in digits:
tmp = []
for an in ans:
for char in kmaps[num]:
tmp.append(an + char)
ans = tmp
return ans
```

- palindrome_partitioning

```
""" It looks like you need to be looking not for all palindromic substrings,
but rather for all the ways you can divide the input string
up into palindromic substrings.
(There's always at least one way,
since one-character substrings are always palindromes.)
ex)
'abcbab' => [['abcba', 'b'], ['a', 'bcb', 'a', 'b'], ['a', 'b', 'c', 'bab'], ['a', 'b', 'c', 'b', 'a', 'b']]
"""
def palindromic_substrings(s):
if not s:
return [[]]
results = []
for i in range(len(s), 0, -1):
sub = s[:i]
if sub == sub[::-1]:
for rest in palindromic_substrings(s[i:]):
results.append([sub] + rest)
return results
"""
There's two loops.
The outer loop checks each length of initial substring
(in descending length order) to see if it is a palindrome.
If so, it recurses on the rest of the string and loops over the returned
values, adding the initial substring to
each item before adding it to the results.
"""
def palindromic_substrings_iter(s):
"""
A slightly more Pythonic approach with a recursive generator
"""
if not s:
yield []
return
for i in range(len(s), 0, -1):
sub = s[:i]
if sub == sub[::-1]:
for rest in palindromic_substrings_iter(s[i:]):
yield [sub] + rest
```

- pattern_match

```
"""
Given a pattern and a string str,
find if str follows the same pattern.
Here follow means a full match, such that there is a bijection between
a letter in pattern and a non-empty substring in str.
Examples:
pattern = "abab", str = "redblueredblue" should return true.
pattern = "aaaa", str = "asdasdasdasd" should return true.
pattern = "aabb", str = "xyzabcxzyabc" should return false.
Notes:
You may assume both pattern and str contains only lowercase letters.
"""
def pattern_match(pattern, string):
"""
:type pattern: str
:type string: str
:rtype: bool
"""
def backtrack(pattern, string, dic):
if len(pattern) == 0 and len(string) > 0:
return False
if len(pattern) == len(string) == 0:
return True
for end in range(1, len(string)-len(pattern)+2):
if pattern[0] not in dic and string[:end] not in dic.values():
dic[pattern[0]] = string[:end]
if backtrack(pattern[1:], string[end:], dic):
return True
del dic[pattern[0]]
elif pattern[0] in dic and dic[pattern[0]] == string[:end]:
if backtrack(pattern[1:], string[end:], dic):
return True
return False
return backtrack(pattern, string, {})
```

- permute

```
"""
Given a collection of distinct numbers, return all possible permutations.
For example,
[1,2,3] have the following permutations:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
"""
def permute(elements):
"""
returns a list with the permuations.
"""
if len(elements) <= 1:
return [elements]
else:
tmp = []
for perm in permute(elements[1:]):
for i in range(len(elements)):
tmp.append(perm[:i] + elements[0:1] + perm[i:])
return tmp
def permute_iter(elements):
"""
iterator: returns a perumation by each call.
"""
if len(elements) <= 1:
yield elements
else:
for perm in permute_iter(elements[1:]):
for i in range(len(elements)):
yield perm[:i] + elements[0:1] + perm[i:]
# DFS Version
def permute_recursive(nums):
def dfs(res, nums, path):
if not nums:
res.append(path)
for i in range(len(nums)):
print(nums[:i]+nums[i+1:])
dfs(res, nums[:i]+nums[i+1:], path+[nums[i]])
res = []
dfs(res, nums, [])
return res
```

- permute_unique

```
"""
Given a collection of numbers that might contain duplicates,
return all possible unique permutations.
For example,
[1,1,2] have the following unique permutations:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
"""
def permute_unique(nums):
perms = [[]]
for n in nums:
new_perms = []
for l in perms:
for i in range(len(l)+1):
new_perms.append(l[:i]+[n]+l[i:])
if i < len(l) and l[i] == n:
break # handles duplication
perms = new_perms
return perms
```

- subsets

```
"""
Given a set of distinct integers, nums, return all possible subsets.
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,3], a solution is:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
"""
def subsets(nums):
"""
O(2**n)
"""
def backtrack(res, nums, stack, pos):
if pos == len(nums):
res.append(list(stack))
else:
# take nums[pos]
stack.append(nums[pos])
backtrack(res, nums, stack, pos+1)
stack.pop()
# dont take nums[pos]
backtrack(res, nums, stack, pos+1)
res = []
backtrack(res, nums, [], 0)
return res
"""
simplified backtrack
def backtrack(res, nums, cur, pos):
if pos >= len(nums):
res.append(cur)
else:
backtrack(res, nums, cur+[nums[pos]], pos+1)
backtrack(res, nums, cur, pos+1)
"""
# Iteratively
def subsets_v2(nums):
res = [[]]
for num in sorted(nums):
res += [item+[num] for item in res]
return res
```

- subsets_unique

```
"""
Given a collection of integers that might contain duplicates, nums,
return all possible subsets.
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,2], a solution is:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
"""
def subsets_unique(nums):
def backtrack(res, nums, stack, pos):
if pos == len(nums):
res.add(tuple(stack))
else:
# take
stack.append(nums[pos])
backtrack(res, nums, stack, pos+1)
stack.pop()
# don't take
backtrack(res, nums, stack, pos+1)
res = set()
backtrack(res, nums, [], 0)
return list(res)
```

- bfs
- maze_search

```
from collections import deque
'''
BFS time complexity : O(|E| + |V|)
BFS space complexity : O(|E| + |V|)
do BFS from (0,0) of the grid and get the minimum number of steps needed to get to the lower right column
only step on the columns whose value is 1
if there is no path, it returns -1
Ex 1)
If grid is
[[1,0,1,1,1,1],
[1,0,1,0,1,0],
[1,0,1,0,1,1],
[1,1,1,0,1,1]],
the answer is: 14
Ex 2)
If grid is
[[1,0,0],
[0,1,1],
[0,1,1]],
the answer is: -1
'''
def maze_search(maze):
BLOCKED, ALLOWED = 0, 1
UNVISITED, VISITED = 0, 1
initial_x, initial_y = 0, 0
if maze[initial_x][initial_y] == BLOCKED:
return -1
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]
height, width = len(maze), len(maze[0])
target_x, target_y = height - 1, width - 1
queue = deque([(initial_x, initial_y, 0)])
is_visited = [[UNVISITED for w in range(width)] for h in range(height)]
is_visited[initial_x][initial_y] = VISITED
while queue:
x, y, steps = queue.popleft()
if x == target_x and y == target_y:
return steps
for dx, dy in directions:
new_x = x + dx
new_y = y + dy
if not (0 <= new_x < height and 0 <= new_y < width):
continue
if maze[new_x][new_y] == ALLOWED and is_visited[new_x][new_y] == UNVISITED:
queue.append((new_x, new_y, steps + 1))
is_visited[new_x][new_y] = VISITED
return -1
```

- shortest_distance_from_all_buildings

```
import collections
"""
do BFS from each building, and decrement all empty place for every building visit
when grid[i][j] == -b_nums, it means that grid[i][j] are already visited from all b_nums
and use dist to record distances from b_nums
"""
def shortest_distance(grid):
if not grid or not grid[0]:
return -1
matrix = [[[0,0] for i in range(len(grid[0]))] for j in range(len(grid))]
count = 0 # count how many building we have visited
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 1:
bfs(grid, matrix, i, j, count)
count += 1
res = float('inf')
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j][1]==count:
res = min(res, matrix[i][j][0])
return res if res!=float('inf') else -1
def bfs(grid, matrix, i, j, count):
q = [(i, j, 0)]
while q:
i, j, step = q.pop(0)
for k, l in [(i-1,j), (i+1,j), (i,j-1), (i,j+1)]:
# only the position be visited by count times will append to queue
if 0<=k<len(grid) and 0<=l<len(grid[0]) and \
matrix[k][l][1]==count and grid[k][l]==0:
matrix[k][l][0] += step+1
matrix[k][l][1] = count+1
q.append((k, l, step+1))
```

- word_ladder

```
"""
Given two words (begin_word and end_word), and a dictionary's word list,
find the length of shortest transformation sequence
from beginWord to endWord, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,
Given:
begin_word = "hit"
end_word = "cog"
word_list = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Note:
Return -1 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
"""
def ladder_length(begin_word, end_word, word_list):
"""
Bidirectional BFS!!!
:type begin_word: str
:type end_word: str
:type word_list: Set[str]
:rtype: int
"""
if len(begin_word) != len(end_word):
return -1 # not possible
if begin_word == end_word:
return 0
# when only differ by 1 character
if sum(c1 != c2 for c1, c2 in zip(begin_word, end_word)) == 1:
return 1
begin_set = set()
end_set = set()
begin_set.add(begin_word)
end_set.add(end_word)
result = 2
while begin_set and end_set:
if len(begin_set) > len(end_set):
begin_set, end_set = end_set, begin_set
next_begin_set = set()
for word in begin_set:
for ladder_word in word_range(word):
if ladder_word in end_set:
return result
if ladder_word in word_list:
next_begin_set.add(ladder_word)
word_list.remove(ladder_word)
begin_set = next_begin_set
result += 1
# print(begin_set)
# print(result)
return -1
def word_range(word):
for ind in range(len(word)):
temp = word[ind]
for c in [chr(x) for x in range(ord('a'), ord('z') + 1)]:
if c != temp:
yield word[:ind] + c + word[ind + 1:]
```

- bit
- add_bitwise_operator

```
"""
The following code adds two positive integers without using the '+' operator.
The code uses bitwise operations to add two numbers.
Input: 2 3
Output: 5
"""
def add_bitwise_operator(x, y):
while y:
carry = x & y
x = x ^ y
y = carry << 1
return x
```

- bit_operation

```
"""
Fundamental bit operation:
get_bit(num, i): get an exact bit at specific index
set_bit(num, i): set a bit at specific index
clear_bit(num, i): clear a bit at specific index
update_bit(num, i, bit): update a bit at specific index
"""
"""
This function shifts 1 over by i bits, creating a value being like 0001000. By
performing an AND with num, we clear all bits other than the bit at bit i.
Finally we compare that to 0
"""
def get_bit(num, i):
return (num & (1 << i)) != 0
"""
This function shifts 1 over by i bits, creating a value being like 0001000. By
performing an OR with num, only value at bit i will change.
"""
def set_bit(num, i):
return num | (1 << i)
"""
This method operates in almost the reverse of set_bit
"""
def clear_bit(num, i):
mask = ~(1 << i)
return num & mask
"""
To set the ith bit to value, we first clear the bit at position i by using a
mask. Then, we shift the intended value. Finally we OR these two numbers
"""
def update_bit(num, i, bit):
mask = ~(1 << i)
return (num & mask) | (bit << i)
```

- bytes_int_conversion

```
from collections import deque
def int_to_bytes_big_endian(num):
bytestr = deque()
while num > 0:
# list.insert(0, ...) is inefficient
bytestr.appendleft(num & 0xff)
num >>= 8
return bytes(bytestr)
def int_to_bytes_little_endian(num):
bytestr = []
while num > 0:
bytestr.append(num & 0xff)
num >>= 8
return bytes(bytestr)
def bytes_big_endian_to_int(bytestr):
num = 0
for b in bytestr:
num <<= 8
num += b
return num
def bytes_little_endian_to_int(bytestr):
num = 0
e = 0
for b in bytestr:
num += b << e
e += 8
return num
```

- count_flips_to_convert

```
"""
Write a function to determine the minimal number of bits you would need to
flip to convert integer A to integer B.
For example:
Input: 29 (or: 11101), 15 (or: 01111)
Output: 2
"""
def count_flips_to_convert(a, b):
diff = a ^ b
# count number of ones in diff
count = 0
while diff:
diff &= (diff - 1)
count += 1
return count
```

- count_ones

```
"""
Write a function that takes an unsigned integer and
returns the number of '1' bits it has
(also known as the Hamming weight).
For example, the 32-bit integer '11' has binary
representation 00000000000000000000000000001011,
so the function should return 3.
T(n)- O(k) : k is the number of 1s present in binary representation.
NOTE: this complexity is better than O(log n).
e.g. for n = 00010100000000000000000000000000
only 2 iterations are required.
Number of loops is
equal to the number of 1s in the binary representation."""
def count_ones_recur(n):
"""Using Brian Kernighan's Algorithm. (Recursive Approach)"""
if not n:
return 0
return 1 + count_ones_recur(n & (n-1))
def count_ones_iter(n):
"""Using Brian Kernighan's Algorithm. (Iterative Approach)"""
count = 0
while n:
n &= (n-1)
count += 1
return count
```

- find_difference

```
"""
Given two strings s and t which consist of only lowercase letters.
String t is generated by random shuffling string s and then add one more letter
at a random position. Find the letter that was added in t.
For example:
Input:
s = "abcd"
t = "abecd"
Output: 'e'
Explanation:
'e' is the letter that was added.
"""
"""
We use the characteristic equation of XOR.
A xor B xor C = A xor C xor B
If A == C, then A xor C = 0
and then, B xor 0 = B
"""
def find_difference(s, t):
ret = 0
for ch in s + t:
# ord(ch) return an integer representing the Unicode code point of that character
ret = ret ^ ord(ch)
# chr(i) Return the string representing a character whose Unicode code point is the integer i
return chr(ret)
```

- find_missing_number

```
"""
Returns the missing number from a sequence of unique integers
in range [0..n] in O(n) time and space. The difference between
consecutive integers cannot be more than 1. If the sequence is
already complete, the next integer in the sequence will be returned.
For example:
Input: nums = [4, 1, 3, 0, 6, 5, 2]
Output: 7
"""
def find_missing_number(nums):
missing = 0
for i, num in enumerate(nums):
missing ^= num
missing ^= i + 1
return missing
def find_missing_number2(nums):
num_sum = sum(nums)
n = len(nums)
total_sum = n*(n+1) // 2
missing = total_sum - num_sum
return missing
```

- flip_bit_longest_sequence

```
"""
You have an integer and you can flip exactly one bit from a 0 to 1.
Write code to find the length of the longest sequence of 1s you could create.
For example:
Input: 1775 ( or: 11011101111)
Output: 8
"""
def flip_bit_longest_seq(num):
curr_len = 0
prev_len = 0
max_len = 0
while num:
if num & 1 == 1: # last digit is 1
curr_len += 1
elif num & 1 == 0: # last digit is 0
if num & 2 == 0: # second last digit is 0
prev_len = 0
else:
prev_len = curr_len
curr_len = 0
max_len = max(max_len, prev_len + curr_len)
num = num >> 1 # right shift num
return max_len + 1
```

- power_of_two

```
"""
given an integer, write a function to determine if it is a power of two
"""
def is_power_of_two(n):
"""
:type n: int
:rtype: bool
"""
return n > 0 and not n & (n-1)
```

- reverse_bits

```
"""
Reverse bits of a given 32 bits unsigned integer.
For example, given input 43261596
(represented in binary as 00000010100101000001111010011100),
return 964176192
(represented in binary as 00111001011110000010100101000000).
"""
def reverse_bits(n):
m = 0
i = 0
while i < 32:
m = (m << 1) + (n & 1)
n >>= 1
i += 1
return m
```

- single_number

```
"""
Given an array of integers, every element appears
twice except for one. Find that single one.
NOTE: This also works for finding a number occurring odd
number of times, where all the other numbers appear
even number of times.
Note:
Your algorithm should have a linear runtime complexity.
Could you implement it without using extra memory?
"""
def single_number(nums):
"""
Returns single number, if found.
Else if all numbers appear twice, returns 0.
:type nums: List[int]
:rtype: int
"""
i = 0
for num in nums:
i ^= num
return i
```

- single_number2

```
"""
Given an array of integers, every element appears
three times except for one, which appears exactly once.
Find that single one.
Note:
Your algorithm should have a linear runtime complexity.
Could you implement it without using extra memory?
Solution:
32 bits for each integer.
Consider 1 bit in it, the sum of each integer's corresponding bit
(except for the single number)
should be 0 if mod by 3. Hence, we sum the bits of all
integers and mod by 3,
the remaining should be the exact bit of the single number.
In this way, you get the 32 bits of the single number.
"""
# Another awesome answer
def single_number2(nums):
ones, twos = 0, 0
for i in range(len(nums)):
ones = (ones ^ nums[i]) & ~twos
twos = (twos ^ nums[i]) & ~ones
return ones
```

- single_number3

```
"""
Given an array of numbers nums,
in which exactly two elements appear only once
and all the other elements appear exactly twice.
Find the two elements that appear only once.
Limitation: Time Complexity: O(N) and Space Complexity O(1)
For example:
Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].
Note:
The order of the result is not important.
So in the above example, [5, 3] is also correct.
Solution:
1. Use XOR to cancel out the pairs and isolate A^B
2. It is guaranteed that at least 1 bit exists in A^B since
A and B are different numbers. ex) 010 ^ 111 = 101
3. Single out one bit R (right most bit in this solution) to use it as a pivot
4. Divide all numbers into two groups.
One group with a bit in the position R
One group without a bit in the position R
5. Use the same strategy we used in step 1 to isolate A and B from each group.
"""
def single_number3(nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
# isolate a^b from pairs using XOR
ab = 0
for n in nums:
ab ^= n
# isolate right most bit from a^b
right_most = ab & (-ab)
# isolate a and b from a^b
a, b = 0, 0
for n in nums:
if n & right_most:
a ^= n
else:
b ^= n
return [a, b]
```

- subsets

```
"""
Given a set of distinct integers, nums,
return all possible subsets.
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,3], a solution is:
{
(1, 2),
(1, 3),
(1,),
(2,),
(3,),
(1, 2, 3),
(),
(2, 3)
}
"""
def subsets(nums):
"""
:param nums: List[int]
:return: Set[tuple]
"""
n = len(nums)
total = 1 << n
res = set()
for i in range(total):
subset = tuple(num for j, num in enumerate(nums) if i & 1 << j)
res.add(subset)
return res
"""
this explanation is from leet_nik @ leetcode
This is an amazing solution. Learnt a lot.
Number of subsets for {1 , 2 , 3 } = 2^3 .
why ?
case possible outcomes for the set of subsets
1 -> Take or dont take = 2
2 -> Take or dont take = 2
3 -> Take or dont take = 2
therefore,
total = 2*2*2 = 2^3 = {{}, {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}}
Lets assign bits to each outcome ->
First bit to 1 , Second bit to 2 and third bit to 3
Take = 1
Dont take = 0
0) 0 0 0 -> Dont take 3 , Dont take 2 , Dont take 1 = { }
1) 0 0 1 -> Dont take 3 , Dont take 2 , take 1 = { 1 }
2) 0 1 0 -> Dont take 3 , take 2 , Dont take 1 = { 2 }
3) 0 1 1 -> Dont take 3 , take 2 , take 1 = { 1 , 2 }
4) 1 0 0 -> take 3 , Dont take 2 , Dont take 1 = { 3 }
5) 1 0 1 -> take 3 , Dont take 2 , take 1 = { 1 , 3 }
6) 1 1 0 -> take 3 , take 2 , Dont take 1 = { 2 , 3 }
7) 1 1 1 -> take 3 , take 2 , take 1 = { 1 , 2 , 3 }
In the above logic ,Insert S[i] only if (j>>i)&1 ==true
{ j E { 0,1,2,3,4,5,6,7 } i = ith element in the input array }
element 1 is inserted only into those places where 1st bit of j is 1
if( j >> 0 &1 ) ==> for above above eg.
this is true for sl.no.( j )= 1 , 3 , 5 , 7
element 2 is inserted only into those places where 2nd bit of j is 1
if( j >> 1 &1 ) == for above above eg.
this is true for sl.no.( j ) = 2 , 3 , 6 , 7
element 3 is inserted only into those places where 3rd bit of j is 1
if( j >> 2 & 1 ) == for above above eg.
this is true for sl.no.( j ) = 4 , 5 , 6 , 7
Time complexity : O(n*2^n) , for every input element loop traverses
the whole solution set length i.e. 2^n
"""
```

- swap_pair

```
"""
Swap_pair: A function swap odd and even bits in an integer with as few instructions
as possible (Ex bit and bit 1 are swapped, bit 2 and bit 3 are swapped)
For example:
22: 010110 --> 41: 101001
10: 1010 --> 5 : 0101
"""
"""
We can approach this as operating on the odds bit first, and then the even bits.
We can mask all odd bits with 10101010 in binary ('AA') then shift them right by 1
Similarly, we mask all even bit with 01010101 in binary ('55') then shift them left
by 1. Finally, we merge these two values by OR operation.
"""
def swap_pair(num):
# odd bit arithmetic right shift 1 bit
odd = (num & int('AAAAAAAA', 16)) >> 1
# even bit left shift 1 bit
even = (num & int('55555555', 16)) << 1
return odd | even
```

- has_alternative_bit

```
"""
Given a positive integer, check whether it has alternating bits: namely,
if two adjacent bits will always have different values.
For example:
Input: 5
Output: True because the binary representation of 5 is: 101.
Input: 7
Output: False because the binary representation of 7 is: 111.
Input: 11
Output: False because the binary representation of 11 is: 1011.
Input: 10
Output: True because The binary representation of 10 is: 1010.
"""
# Time Complexity - O(number of bits in n)
def has_alternative_bit(n):
first_bit = 0
second_bit = 0
while n:
first_bit = n & 1
if n >> 1:
second_bit = (n >> 1) & 1
if not first_bit ^ second_bit:
return False
else:
return True
n = n >> 1
return True
# Time Complexity - O(1)
def has_alternative_bit_fast(n):
mask1 = int('aaaaaaaa', 16) # for bits ending with zero (...1010)
mask2 = int('55555555', 16) # for bits ending with one (...0101)
return mask1 == (n + (n ^ mask1)) or mask2 == (n + (n ^ mask2))
```

- insert_bit

```
"""
Insertion:
insert_one_bit(num, bit, i): insert exact one bit at specific position
For example:
Input: num = 10101 (21)
insert_one_bit(num, 1, 2): 101101 (45)
insert_one_bit(num, 0, 2): 101001 (41)
insert_one_bit(num, 1, 5): 110101 (53)
insert_one_bit(num, 1, 0): 101011 (43)
insert_mult_bits(num, bits, len, i): insert multiple bits with len at specific position
For example:
Input: num = 101 (5)
insert_mult_bits(num, 7, 3, 1): 101111 (47)
insert_mult_bits(num, 7, 3, 0): 101111 (47)
insert_mult_bits(num, 7, 3, 3): 111101 (61)
"""
"""
Insert exact one bit at specific position
Algorithm:
1. Create a mask having bit from i to the most significant bit, and append the new bit at 0 position
2. Keep the bit from 0 position to i position ( like 000...001111)
3. Merge mask and num
"""
def insert_one_bit(num, bit, i):
# Create mask
mask = num >> i
mask = (mask << 1) | bit
mask = mask << i
# Keep the bit from 0 position to i position
right = ((1 << i) - 1) & num
return right | mask
def insert_mult_bits(num, bits, len, i):
mask = num >> i
mask = (mask << len) | bits
mask = mask << i
right = ((1 << i) - 1) & num
return right | mask
```

- remove_bit

```
"""
Remove_bit(num, i): remove a bit at specific position.
For example:
Input: num = 10101 (21)
remove_bit(num, 2): output = 1001 (9)
remove_bit(num, 4): output = 101 (5)
remove_bit(num, 0): output = 1010 (10)
"""
def remove_bit(num, i):
mask = num >> (i + 1)
mask = mask << i
right = ((1 << i) - 1) & num
return mask | right
```

- binary_gap

```
"""
Given a positive integer N, find and return the longest distance between two
consecutive 1' in the binary representation of N.
If there are not two consecutive 1's, return 0
For example:
Input: 22
Output: 2
Explanation:
22 in binary is 10110
In the binary representation of 22, there are three ones, and two consecutive pairs of 1's.
The first consecutive pair of 1's have distance 2.
The second consecutive pair of 1's have distance 1.
The answer is the largest of these two distances, which is 2
"""
# 原方法为 binary_gap，但通过实验发现算法有误，不论是什么数，输出值最多为2。
# 改进方法为binary_gap_improved。
# The original method is binary_gap,
# but the experimental results show that the algorithm seems to be wrong,
# regardless of the number, the output value is up to 2.
# The improved method is binary_gap_improved.
def binary_gap(N):
last = None
ans = 0
index = 0
while N != 0:
if N & 1:
if last is not None:
ans = max(ans, index - last)
last = index
index = index + 1
N = N >> 1
return ans
def binary_gap_improved(N):
last = None
ans = 0
index = 0
while N != 0:
tes = N & 1
if tes:
if last is not None:
ans = max(ans, index - last + 1)
else:
last = index
else:
last = index + 1
index = index + 1
N = N >> 1
return ans
print(binary_gap(111))
print(binary_gap_improved(111))
```

- compression
- huffman_coding

```
"""
Huffman coding is an efficient method of compressing data without losing information.
This algorithm analyzes the symbols that appear in a message.
Symbols that appear more often will be encoded as a shorter-bit string
while symbols that aren't used as much will be encoded as longer strings.
"""
from collections import defaultdict, deque
import heapq
class Node:
def __init__(self, frequency=0, sign=None, left=None, right=None):
self.frequency = frequency
self.sign = sign
self.left = left
self.right = right
def __lt__(self, other):
return self.frequency < other.frequency
def __gt__(self, other):
return self.frequency > other.frequency
def __eq__(self, other):
return self.frequency == other.frequency
def __str__(self):
return "<ch: {0}: {1}>".format(self.sign, self.frequency)
def __repr__(self):
return "<ch: {0}: {1}>".format(self.sign, self.frequency)
class HuffmanReader:
def __init__(self, file):
self.file = file
self.buffer = []
self.is_last_byte = False
def get_number_of_additional_bits_in_the_last_byte(self) -> int:
bin_num = self.get_bit() + self.get_bit() + self.get_bit()
return int(bin_num, 2)
def load_tree(self) -> Node:
"""
Load tree from file
:return:
"""
node_stack = deque()
queue_leaves = deque()
root = Node()
current_node = root
is_end_of_tree = False
while not is_end_of_tree:
current_bit = self.get_bit()
if current_bit == "0":
current_node.left = Node()
current_node.right = Node()
node_stack.append(current_node.right) # going to left node, right push on stack
current_node = current_node.left
else:
queue_leaves.append(current_node)
if node_stack:
current_node = node_stack.pop()
else:
is_end_of_tree = True
self._fill_tree(queue_leaves)
return root
def _fill_tree(self, leaves_queue):
"""
Load values to tree after reading tree
:param leaves_queue:
:return:
"""
leaves_queue.reverse()
while leaves_queue:
node = leaves_queue.pop()
s = int(self.get_byte(), 2)
node.sign = s
def _load_byte(self, buff_limit=8) -> bool:
"""
Load next byte is buffer is less than buff_limit
:param buff_limit:
:return: True if there is enough bits in buffer to read
"""
if len(self.buffer) <= buff_limit:
byte = self.file.read(1)
if not byte:
return False
i = int.from_bytes(byte, "big")
self.buffer.extend(list("{0:08b}".format(i)))
return True
def get_bit(self, buff_limit=8):
if self._load_byte(buff_limit):
bit = self.buffer.pop(0)
return bit
else:
return -1
def get_byte(self):
if self._load_byte():
byte_list = self.buffer[:8]
self.buffer = self.buffer[8:]
return "".join(byte_list)
else:
return -1
class HuffmanWriter:
def __init__(self, file):
self.file = file
self.buffer = ""
self.saved_bits = 0
def write_char(self, char):
self.write_int(ord(char))
def write_int(self, num):
bin_int = "{0:08b}".format(num)
self.write_bits(bin_int)
def write_bits(self, bits):
self.saved_bits += len(bits)
self.buffer += bits
while len(self.buffer) >= 8:
i = int(self.buffer[:8], 2)
self.file.write(bytes([i]))
self.buffer = self.buffer[8:]
def save_tree(self, tree):
"""
Generate and save tree code to file
:param tree:
:return:
"""
signs = []
tree_code = ""
def get_code_tree(T):
nonlocal tree_code
if T.sign is not None:
signs.append(T.sign)
if T.left:
tree_code += "0"
get_code_tree(T.left)
if T.right:
tree_code += "1"
get_code_tree(T.right)
get_code_tree(tree)
self.write_bits(tree_code + "1") # "1" indicates that tree ended (it will be needed to load the tree)
for int_sign in signs:
self.write_int(int_sign)
def _save_information_about_additional_bits(self, additional_bits: int):
"""
Overwrite first three bits in the file
:param additional_bits: number of bits that were appended to fill last byte
:return:
"""
self.file.seek(0)
first_byte_raw = self.file.read(1)
self.file.seek(0)
first_byte = "{0:08b}".format(int.from_bytes(first_byte_raw, "big"))
# overwrite first three bits
first_byte = first_byte[3:]
first_byte = "{0:03b}".format(additional_bits) + first_byte
self.write_bits(first_byte)
def close(self):
additional_bits = 8 - len(self.buffer)
if additional_bits != 8: # buffer is empty, no need to append extra "0"
self.write_bits("0" * additional_bits)
self._save_information_about_additional_bits(additional_bits)
class TreeFinder:
"""
Class to help find signs in tree
"""
def __init__(self, tree):
self.root = tree
self.current_node = tree
self.found = None
def find(self, bit):
"""
Find sign in tree
:param bit:
:return: True if sign is found
"""
if bit == "0":
self.current_node = self.current_node.left
elif bit == "1":
self.current_node = self.current_node.right
else:
self._reset()
return True
if self.current_node.sign is not None:
self._reset(self.current_node.sign)
return True
else:
return False
def _reset(self, found=""):
self.found = found
self.current_node = self.root
class HuffmanCoding:
def __init__(self):
pass
@staticmethod
def decode_file(file_in_name, file_out_name):
with open(file_in_name, "rb") as file_in, open(file_out_name, "wb") as file_out:
reader = HuffmanReader(file_in)
additional_bits = reader.get_number_of_additional_bits_in_the_last_byte()
tree = reader.load_tree()
HuffmanCoding._decode_and_write_signs_to_file(file_out, reader, tree, additional_bits)
print("File decoded.")
@staticmethod
def _decode_and_write_signs_to_file(file, reader: HuffmanReader, tree: Node, additional_bits: int):
tree_finder = TreeFinder(tree)
is_end_of_file = False
while not is_end_of_file:
bit = reader.get_bit()
if bit != -1:
while not tree_finder.find(bit): # read whole code
bit = reader.get_bit(0)
file.write(bytes([tree_finder.found]))
else: # There is last byte in buffer to parse
is_end_of_file = True
last_byte = reader.buffer
last_byte = last_byte[:-additional_bits] # remove additional "0" used to fill byte
for bit in last_byte:
if tree_finder.find(bit):
file.write(bytes([tree_finder.found]))
@staticmethod
def encode_file(file_in_name, file_out_name):
with open(file_in_name, "rb") as file_in, open(file_out_name, mode="wb+") as file_out:
signs_frequency = HuffmanCoding._get_char_frequency(file_in)
file_in.seek(0)
tree = HuffmanCoding._create_tree(signs_frequency)
codes = HuffmanCoding._generate_codes(tree)
writer = HuffmanWriter(file_out)
writer.write_bits("000") # leave space to save how many bits will be appended to fill the last byte
writer.save_tree(tree)
HuffmanCoding._encode_and_write_signs_to_file(file_in, writer, codes)
writer.close()
print("File encoded.")
@staticmethod
def _encode_and_write_signs_to_file(file, writer: HuffmanWriter, codes: dict):
sign = file.read(1)
while sign:
int_char = int.from_bytes(sign, "big")
writer.write_bits(codes[int_char])
sign = file.read(1)
@staticmethod
def _get_char_frequency(file) -> dict:
is_end_of_file = False
signs_frequency = defaultdict(lambda: 0)
while not is_end_of_file:
prev_pos = file.tell()
sign = file.read(1)
curr_pos = file.tell()
if prev_pos == curr_pos:
is_end_of_file = True
else:
signs_frequency[int.from_bytes(sign, "big")] += 1
return signs_frequency
@staticmethod
def _generate_codes(tree: Node) -> dict:
codes = dict()
HuffmanCoding._go_through_tree_and_create_codes(tree, "", codes)
return codes
@staticmethod
def _create_tree(signs_frequency: dict) -> Node:
nodes = [Node(frequency=frequency, sign=char_int) for char_int, frequency in signs_frequency.items()]
heapq.heapify(nodes)
while len(nodes) > 1:
left = heapq.heappop(nodes)
right = heapq.heappop(nodes)
new_node = Node(frequency=left.frequency + right.frequency, left=left, right=right)
heapq.heappush(nodes, new_node)
return nodes[0] # root
@staticmethod
def _go_through_tree_and_create_codes(tree: Node, code: str, dict_codes: dict):
if tree.sign is not None:
dict_codes[tree.sign] = code
if tree.left:
HuffmanCoding._go_through_tree_and_create_codes(tree.left, code + "0", dict_codes)
if tree.right:
HuffmanCoding._go_through_tree_and_create_codes(tree.right, code + "1", dict_codes)
```

- rle_compression

```
"""
Run-length encoding (RLE) is a simple compression algorithm
that gets a stream of data as the input and returns a
sequence of counts of consecutive data values in a row.
When decompressed the data will be fully recovered as RLE
is a lossless data compression.
"""
def encode_rle(input):
"""
Gets a stream of data and compresses it
under a Run-Length Encoding.
:param input: The data to be encoded.
:return: The encoded string.
"""
if not input: return ''
encoded_str = ''
prev_ch = ''
count = 1
for ch in input:
# Check If the subsequent character does not match
if ch != prev_ch:
# Add the count and character
if prev_ch:
encoded_str += str(count) + prev_ch
# Reset the count and set the character
count = 1
prev_ch = ch
else:
# Otherwise increment the counter
count += 1
else:
return encoded_str + (str(count) + prev_ch)
def decode_rle(input):
"""
Gets a stream of data and decompresses it
under a Run-Length Decoding.
:param input: The data to be decoded.
:return: The decoded string.
"""
decode_str = ''
count = ''
for ch in input:
# If not numerical
if not ch.isdigit():
# Expand it for the decoding
decode_str += ch * int(count)
count = ''
else:
# Add it in the counter
count += ch
return decode_str
```

- elias

```
"""
Elias γ code or Elias gamma code is a universal code encoding positive integers.
It is used most commonly when coding integers whose upper-bound cannot be determined beforehand.
Elias δ code or Elias delta code is a universal code encoding the positive integers,
that includes Elias γ code when calculating.
Both were developed by Peter Elias.
"""
from math import log,ceil
log2 = lambda x: log(x,2)
# Calculates the binary number
def binary(x,l=1):
fmt = '{0:0%db}' % l
return fmt.format(x)
# Calculates the unary number
def unary(x):
return (x-1)*'1'+'0'
def elias_generic(lencoding, x):
"""
The compressed data is calculated in two parts.
The first part is the unary number of 1 + ⌊log2(x)⌋.
The second part is the binary number of x - 2^(⌊log2(x)⌋).
For the final result we add these two parts.
"""
if x == 0: return '0'
first_part = 1 + int(log2(x))
a = x - 2**(int(log2(x)))
k = int(log2(x))
return lencoding(first_part) + binary(a,k)
def elias_gamma(x):
"""
For the first part we put the unary number of x.
"""
return elias_generic(unary, x)
def elias_delta(x):
"""
For the first part we put the elias_g of the number.
"""
return elias_generic(elias_gamma,x)
```

- dfs
- all_factors

```
"""
Numbers can be regarded as product of its factors. For example,
8 = 2 x 2 x 2;
= 2 x 4.
Write a function that takes an integer n and return all possible combinations
of its factors.Numbers can be regarded as product of its factors. For example,
8 = 2 x 2 x 2;
= 2 x 4.
Examples:
input: 1
output:
[]
input: 37
output:
[]
input: 32
output:
[
[2, 16],
[2, 2, 8],
[2, 2, 2, 4],
[2, 2, 2, 2, 2],
"""
def get_factors(n):
"""[summary]
Arguments:
n {[int]} -- [to analysed number]
Returns:
[list of lists] -- [all factors of the number n]
"""
def factor(n, i, combi, res):
"""[summary]
helper function
Arguments:
n {[int]} -- [number]
i {[int]} -- [to tested divisor]
combi {[list]} -- [catch divisors]
res {[list]} -- [all factors of the number n]
Returns:
[list] -- [res]
"""
while i * i <= n:
if n % i == 0:
res += combi + [i, int(n/i)],
factor(n/i, i, combi+[i], res)
i += 1
return res
return factor(n, 2, [], [])
def get_factors_iterative1(n):
"""[summary]
Computes all factors of n.
Translated the function get_factors(...) in
a call-stack modell.
Arguments:
n {[int]} -- [to analysed number]
Returns:
[list of lists] -- [all factors]
"""
todo, res = [(n, 2, [])], []
while todo:
n, i, combi = todo.pop()
while i * i <= n:
if n % i == 0:
res += combi + [i, n//i],
todo.append((n//i, i, combi+[i])),
i += 1
return res
def get_factors_iterative2(n):
"""[summary]
analog as above
Arguments:
n {[int]} -- [description]
Returns:
[list of lists] -- [all factors of n]
"""
ans, stack, x = [], [], 2
while True:
if x > n // x:
if not stack:
return ans
ans.append(stack + [n])
x = stack.pop()
n *= x
x += 1
elif n % x == 0:
stack.append(x)
n //= x
else:
x += 1
```

- count_islands

```
"""
Given a 2d grid map of '1's (land) and '0's (water),
count the number of islands.
An island is surrounded by water and is formed by
connecting adjacent lands horizontally or vertically.
You may assume all four edges of the grid are all surrounded by water.
Example 1:
11110
11010
11000
00000
Answer: 1
Example 2:
11000
11000
00100
00011
Answer: 3
"""
def num_islands(grid):
count = 0
for i in range(len(grid)):
for j, col in enumerate(grid[i]):
if col == 1:
dfs(grid, i, j)
count += 1
return count
def dfs(grid, i, j):
if (i < 0 or i >= len(grid)) or (j < 0 or j >= len(grid[0])):
return
if grid[i][j] != 1:
return
grid[i][j] = 0
dfs(grid, i+1, j)
dfs(grid, i-1, j)
dfs(grid, i, j+1)
dfs(grid, i, j-1)
```

- pacific_atlantic

```
# Given an m x n matrix of non-negative integers representing
# the height of each unit cell in a continent,
# the "Pacific ocean" touches the left and top edges of the matrix
# and the "Atlantic ocean" touches the right and bottom edges.
# Water can only flow in four directions (up, down, left, or right)
# from a cell to another one with height equal or lower.
# Find the list of grid coordinates where water can flow to both the
# Pacific and Atlantic ocean.
# Note:
# The order of returned grid coordinates does not matter.
# Both m and n are less than 150.
# Example:
# Given the following 5x5 matrix:
# Pacific ~ ~ ~ ~ ~
# ~ 1 2 2 3 (5) *
# ~ 3 2 3 (4) (4) *
# ~ 2 4 (5) 3 1 *
# ~ (6) (7) 1 4 5 *
# ~ (5) 1 1 2 4 *
# * * * * * Atlantic
# Return:
# [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]]
# (positions with parentheses in above matrix).
def pacific_atlantic(matrix):
"""
:type matrix: List[List[int]]
:rtype: List[List[int]]
"""
n = len(matrix)
if not n: return []
m = len(matrix[0])
if not m: return []
res = []
atlantic = [[False for _ in range (n)] for _ in range(m)]
pacific = [[False for _ in range (n)] for _ in range(m)]
for i in range(n):
dfs(pacific, matrix, float("-inf"), i, 0)
dfs(atlantic, matrix, float("-inf"), i, m-1)
for i in range(m):
dfs(pacific, matrix, float("-inf"), 0, i)
dfs(atlantic, matrix, float("-inf"), n-1, i)
for i in range(n):
for j in range(m):
if pacific[i][j] and atlantic[i][j]:
res.append([i, j])
return res
def dfs(grid, matrix, height, i, j):
if i < 0 or i >= len(matrix) or j < 0 or j >= len(matrix[0]):
return
if grid[i][j] or matrix[i][j] < height:
return
grid[i][j] = True
dfs(grid, matrix, matrix[i][j], i-1, j)
dfs(grid, matrix, matrix[i][j], i+1, j)
dfs(grid, matrix, matrix[i][j], i, j-1)
dfs(grid, matrix, matrix[i][j], i, j+1)
```

- sudoku_solver

```
"""
It's similar to how human solve Sudoku.
create a hash table (dictionary) val to store possible values in every location.
Each time, start from the location with fewest possible values, choose one value
from it and then update the board and possible values at other locations.
If this update is valid, keep solving (DFS). If this update is invalid (leaving
zero possible values at some locations) or this value doesn't lead to the
solution, undo the updates and then choose the next value.
Since we calculated val at the beginning and start filling the board from the
location with fewest possible values, the amount of calculation and thus the
runtime can be significantly reduced:
The run time is 48-68 ms on LeetCode OJ, which seems to be among the fastest
python solutions here.
The PossibleVals function may be further simplified/optimized, but it works just
fine for now. (it would look less lengthy if we are allowed to use numpy array
for the board lol).
"""
class Sudoku:
def __init__ (self, board, row, col):
self.board = board
self.row = row
self.col = col
self.val = self.possible_values()
def possible_values(self):
a = "123456789"
d, val = {}, {}
for i in range(self.row):
for j in range(self.col):
ele = self.board[i][j]
if ele != ".":
d[("r", i)] = d.get(("r", i), []) + [ele]
d[("c", j)] = d.get(("c", j), []) + [ele]
d[(i//3, j//3)] = d.get((i//3, j//3), []) + [ele]
else:
val[(i,j)] = []
for (i,j) in val.keys():
inval = d.get(("r",i),[])+d.get(("c",j),[])+d.get((i/3,j/3),[])
val[(i,j)] = [n for n in a if n not in inval ]
return val
def solve(self):
if len(self.val)==0:
return True
kee = min(self.val.keys(), key=lambda x: len(self.val[x]))
nums = self.val[kee]
for n in nums:
update = {kee:self.val[kee]}
if self.valid_one(n, kee, update): # valid choice
if self.solve(): # keep solving
return True
self.undo(kee, update) # invalid choice or didn't solve it => undo
return False
def valid_one(self, n, kee, update):
self.board[kee[0]][kee[1]] = n
del self.val[kee]
i, j = kee
for ind in self.val.keys():
if n in self.val[ind]:
if ind[0]==i or ind[1]==j or (ind[0]/3,ind[1]/3)==(i/3,j/3):
update[ind] = n
self.val[ind].remove(n)
if len(self.val[ind])==0:
return False
return True
def undo(self, kee, update):
self.board[kee[0]][kee[1]]="."
for k in update:
if k not in self.val:
self.val[k]= update[k]
else:
self.val[k].append(update[k])
return None
def __str__(self):
"""[summary]
Generates a board representation as string.
Returns:
[str] -- [board representation]
"""
resp = ""
for i in range(self.row):
for j in range(self.col):
resp += " {0} ".format(self.board[i][j])
resp += "\n"
return resp
```

- walls_and_gates

```
"""
You are given a m x n 2D grid initialized with these three possible values:
-1: A wall or an obstacle.
0: A gate.
INF: Infinity means an empty room. We use the value 2^31 - 1 = 2147483647 to represent INF
as you may assume that the distance to a gate is less than 2147483647.
Fill the empty room with distance to its nearest gate.
If it is impossible to reach a gate, it should be filled with INF.
For example, given the 2D grid:
INF -1 0 INF
INF INF INF -1
INF -1 INF -1
0 -1 INF INF
After running your function, the 2D grid should be:
3 -1 0 1
2 2 1 -1
1 -1 2 -1
0 -1 3 4
"""
def walls_and_gates(rooms):
for i in range(len(rooms)):
for j in range(len(rooms[0])):
if rooms[i][j] == 0:
dfs(rooms, i, j, 0)
def dfs(rooms, i, j, depth):
if (i < 0 or i >= len(rooms)) or (j < 0 or j >= len(rooms[0])):
return # out of bounds
if rooms[i][j] < depth:
return # crossed
rooms[i][j] = depth
dfs(rooms, i+1, j, depth+1)
dfs(rooms, i-1, j, depth+1)
dfs(rooms, i, j+1, depth+1)
dfs(rooms, i, j-1, depth+1)
```

- distribution
- histogram

```
"""
Histogram function.
Histogram is an accurate representation of the distribution of numerical data.
It is an estimate of the probability distribution of a continuous variable.
https://en.wikipedia.org/wiki/Histogram
Example:
list_1 = [3, 3, 2, 1]
:return {1: 1, 2: 1, 3: 2}
list_2 = [2, 3, 5, 5, 5, 6, 4, 3, 7]
:return {2: 1, 3: 2, 4: 1, 5: 3, 6: 1, 7: 1}
"""
def get_histogram(input_list: list) -> dict:
"""
Get histogram representation
:param input_list: list with different and unordered values
:return histogram: dict with histogram of input_list
"""
# Create dict to store histogram
histogram = {}
# For each list value, add one to the respective histogram dict position
for i in input_list:
histogram[i] = histogram.get(i, 0) + 1
return histogram
```

- dp
- buy_sell_stock

```
"""
Say you have an array for which the ith element
is the price of a given stock on day i.
If you were only permitted to complete at most one transaction
(ie, buy one and sell one share of the stock),
design an algorithm to find the maximum profit.
Example 1:
Input: [7, 1, 5, 3, 6, 4]
Output: 5
max. difference = 6-1 = 5
(not 7-1 = 6, as selling price needs to be larger than buying price)
Example 2:
Input: [7, 6, 4, 3, 1]
Output: 0
In this case, no transaction is done, i.e. max profit = 0.
"""
# O(n^2) time
def max_profit_naive(prices):
"""
:type prices: List[int]
:rtype: int
"""
max_so_far = 0
for i in range(0, len(prices) - 1):
for j in range(i + 1, len(prices)):
max_so_far = max(max_so_far, prices[j] - prices[i])
return max_so_far
# O(n) time
def max_profit_optimized(prices):
"""
input: [7, 1, 5, 3, 6, 4]
diff : [X, -6, 4, -2, 3, -2]
:type prices: List[int]
:rtype: int
"""
cur_max, max_so_far = 0, 0
for i in range(1, len(prices)):
cur_max = max(0, cur_max + prices[i] - prices[i-1])
max_so_far = max(max_so_far, cur_max)
return max_so_far
```

- climbing_stairs

```
"""
You are climbing a stair case.
It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps.
In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.
"""
# O(n) space
def climb_stairs(n):
"""
:type n: int
:rtype: int
"""
arr = [1, 1]
for _ in range(1, n):
arr.append(arr[-1] + arr[-2])
return arr[-1]
# the above function can be optimized as:
# O(1) space
def climb_stairs_optimized(n):
a = b = 1
for _ in range(n):
a, b = b, a + b
return a
```

- coin_change

```
"""
Problem
Given a value n, if we want to make change for N cents, and we have infinite supply of each of
coins = {S1, S2, .. , Sm} valued coins, how many ways can we make the change?
The order of coins doesn't matter.
For example, for n = 4 and coins = [1, 2, 3], there are four solutions:
[1, 1, 1, 1], [1, 1, 2], [2, 2], [1, 3].
So output should be 4.
For n = 10 and coins = [2, 5, 3, 6], there are five solutions:
[2, 2, 2, 2, 2], [2, 2, 3, 3], [2, 2, 6], [2, 3, 5] and [5, 5].
So the output should be 5.
Time complexity: O(n * m) where n is the value and m is the number of coins
Space complexity: O(n)
"""
def count(coins, n):
# initialize dp array and set base case as 1
dp = [1] + [0] * n
# fill dp in a bottom up manner
for coin in coins:
for i in range(coin, n+1):
dp[i] += dp[i-coin]
return dp[n]
```

- combination_sum

```
"""
Given an integer array with all positive numbers and no duplicates,
find the number of possible combinations that
add up to a positive integer target.
Example:
nums = [1, 2, 3]
target = 4
The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
Note that different sequences are counted as different combinations.
Therefore the output is 7.
Follow up:
What if negative numbers are allowed in the given array?
How does it change the problem?
What limitation we need to add to the question to allow negative numbers?
"""
dp = None
def helper_topdown(nums, target):
global dp
if dp[target] != -1:
return dp[target]
res = 0
for i in range(0, len(nums)):
if target >= nums[i]:
res += helper_topdown(nums, target - nums[i])
dp[target] = res
return res
def combination_sum_topdown(nums, target):
global dp
dp = [-1] * (target + 1)
dp[0] = 1
return helper_topdown(nums, target)
# EDIT: The above solution is top-down. How about a bottom-up one?
def combination_sum_bottom_up(nums, target):
comb = [0] * (target + 1)
comb[0] = 1
for i in range(0, len(comb)):
for j in range(len(nums)):
if i - nums[j] >= 0:
comb[i] += comb[i - nums[j]]
return comb[target]
```

- egg_drop

```
"""
You are given K eggs, and you have access to a building with N floors
from 1 to N. Each egg is identical in function, and if an egg breaks,
you cannot drop it again. You know that there exists a floor F with
0 <= F <= N such that any egg dropped at a floor higher than F will
break, and any egg dropped at or below floor F will not break.
Each move, you may take an egg (if you have an unbroken one) and drop
it from any floor X (with 1 <= X <= N). Your goal is to know with
certainty what the value of F is. What is the minimum number of moves
that you need to know with certainty what F is, regardless of the
initial value of F?
Example:
Input: K = 1, N = 2
Output: 2
Explanation:
Drop the egg from floor 1. If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2. If it breaks, we know with
certainty that F = 1.
If it didn't break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.
"""
# A Dynamic Programming based Python Program for the Egg Dropping Puzzle
INT_MAX = 32767
def egg_drop(n, k):
# A 2D table where entery eggFloor[i][j] will represent minimum
# number of trials needed for i eggs and j floors.
egg_floor = [[0 for x in range(k+1)] for x in range(n+1)]
# We need one trial for one floor and 0 trials for 0 floors
for i in range(1, n+1):
egg_floor[i][1] = 1
egg_floor[i][0] = 0
# We always need j trials for one egg and j floors.
for j in range(1, k+1):
egg_floor[1][j] = j
# Fill rest of the entries in table using optimal substructure
# property
for i in range(2, n+1):
for j in range(2, k+1):
egg_floor[i][j] = INT_MAX
for x in range(1, j+1):
res = 1 + max(egg_floor[i-1][x-1], egg_floor[i][j-x])
if res < egg_floor[i][j]:
egg_floor[i][j] = res
# eggFloor[n][k] holds the result
return egg_floor[n][k]
```

- house_robber

```
"""
You are a professional robber planning to rob houses along a street.
Each house has a certain amount of money stashed,
the only constraint stopping you from robbing each of them
is that adjacent houses have security system connected and
it will automatically contact the police if two adjacent houses
were broken into on the same night.
Given a list of non-negative integers representing the amount of money
of each house, determine the maximum amount of money you
can rob tonight without alerting the police.
"""
def house_robber(houses):
last, now = 0, 0
for house in houses:
last, now = now, max(last + house, now)
return now
```

- int_divide

```
"""
Given positive integer n, find an algorithm to find the number of non-negative number division, or descomposition.
The complexity is O(n^2).
Example 1:
Input: 4
Output: 5
Explaination:
4=4
4=3+1
4=2+2
4=2+1+1
4=1+1+1+1
Example :
Input: 7
Output: 15
Explaination:
7=7
7=6+1
7=5+2
7=5+1+1
7=4+3
7=4+2+1
7=4+1+1+1
7=3+3+1
7=3+2+2
7=3+2+1+1
7=3+1+1+1+1
7=2+2+2+1
7=2+2+1+1+1
7=2+1+1+1+1+1
7=1+1+1+1+1+1+1
"""
def int_divide(n):
arr = [[0 for i in range(n + 1)] for j in range(n + 1)]
arr[1][1] = 1
for i in range(1, n + 1):
for j in range(1, n + 1):
if i < j:
arr[i][j] = arr[i][i]
elif i == j:
arr[i][j] = 1 + arr[i][j - 1]
else:
arr[i][j] = arr[i][j - 1] + arr[i - j][j]
return arr[n][n]
```

- job_scheduling

```
# Python program for weighted job scheduling using Dynamic
# Programming and Binary Search
# Class to represent a job
class Job:
def __init__(self, start, finish, profit):
self.start = start
self.finish = finish
self.profit = profit
# A Binary Search based function to find the latest job
# (before current job) that doesn't conflict with current
# job. "index" is index of the current job. This function
# returns -1 if all jobs before index conflict with it.
# The array jobs[] is sorted in increasing order of finish
# time.
def binary_search(job, start_index):
# Initialize 'lo' and 'hi' for Binary Search
lo = 0
hi = start_index - 1
# Perform binary Search iteratively
while lo <= hi:
mid = (lo + hi) // 2
if job[mid].finish <= job[start_index].start:
if job[mid + 1].finish <= job[start_index].start:
lo = mid + 1
else:
return mid
else:
hi = mid - 1
return -1
# The main function that returns the maximum possible
# profit from given array of jobs
def schedule(job):
# Sort jobs according to finish time
job = sorted(job, key = lambda j: j.finish)
# Create an array to store solutions of subproblems. table[i]
# stores the profit for jobs till arr[i] (including arr[i])
n = len(job)
table = [0 for _ in range(n)]
table[0] = job[0].profit
# Fill entries in table[] using recursive property
for i in range(1, n):
# Find profit including the current job
incl_prof = job[i].profit
l = binary_search(job, i)
if (l != -1):
incl_prof += table[l]
# Store maximum of including and excluding
table[i] = max(incl_prof, table[i - 1])
return table[n-1]
```

- knapsack

```
"""
Given the capacity of the knapsack and items specified by weights and values,
return the maximum summarized value of the items that can be fit in the
knapsack.
Example:
capacity = 5, items(value, weight) = [(60, 5), (50, 3), (70, 4), (30, 2)]
result = 80 (items valued 50 and 30 can both be fit in the knapsack)
The time complexity is O(n * m) and the space complexity is O(m), where n is
the total number of items and m is the knapsack's capacity.
"""
class Item:
def __init__(self, value, weight):
self.value = value
self.weight = weight
def get_maximum_value(items, capacity):
dp = [0] * (capacity + 1)
for item in items:
for cur_weight in reversed(range(item.weight, capacity+1)):
dp[cur_weight] = max(dp[cur_weight], item.value + dp[cur_weight - item.weight])
return dp[capacity]
```

- longest_increasing

```
"""
Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Time complexity:
First algorithm is O(n^2).
Second algorithm is O(nlogx) where x is the max element in the list
Third algorithm is O(nlogn)
Space complexity:
First algorithm is O(n)
Second algorithm is O(x) where x is the max element in the list
Third algorithm is O(n)
"""
def longest_increasing_subsequence(sequence):
"""
Dynamic Programming Algorithm for
counting the length of longest increasing subsequence
type sequence: list[int]
rtype: int
"""
length = len(sequence)
counts = [1 for _ in range(length)]
for i in range(1, length):
for j in range(0, i):
if sequence[i] > sequence[j]:
counts[i] = max(counts[i], counts[j] + 1)
print(counts)
return max(counts)
def longest_increasing_subsequence_optimized(sequence):
"""
Optimized dynamic programming algorithm for
couting the length of the longest increasing subsequence
using segment tree data structure to achieve better complexity
if max element is larger than 10^5 then use
longest_increasing_subsequence_optimied2() instead
type sequence: list[int]
rtype: int
"""
mx = max(sequence)
tree = [0] * (mx<<2)
def update(p, l, r, i, v):
if l == r:
tree[p] = v
return
mid = (l+r)>>1
if i <= mid:
update(p<<1, l, mid, i, v)
else:
update((p<<1)|1, mid+1, r, i, v)
tree[p] = max(tree[p<<1], tree[(p<<1)|1])
def get_max(p, l, r, s, e):
if l > e or r < s:
return 0
if l >= s and r <= e:
return tree[p]
mid = (l+r)>>1
return max(get_max(p<<1, l, mid, s, e), get_max((p<<1)|1, mid+1, r, s, e))
ans = 0
for x in sequence:
cur = get_max(1, 0, mx, 0, x-1)+1
ans = max(ans, cur)
update(1, 0, mx, x, cur)
return ans
def longest_increasing_subsequence_optimized2(sequence):
"""
Optimized dynamic programming algorithm for
counting the length of the longest increasing subsequence
using segment tree data structure to achieve better complexity
type sequence: list[int]
rtype: int
"""
n = len(sequence)
tree = [0] * (n<<2)
sorted_seq = sorted((x, -i) for i, x in enumerate(sequence))
def update(p, l, r, i, v):
if l ==r:
tree[p] = v
return
mid = (l+r)>>1
if i <= mid:
update(p<<1, l, mid, i, v)
else:
update((p<<1)|1, mid+1, r, i, v)
tree[p] = max(tree[p<<1], tree[(p<<1)|1])
def get_max(p, l, r, s, e):
if l > e or r < s:
return 0
if l >= s and r <= e:
return tree[p]
mid = (l+r)>>1
return max(get_max(p<<1, l, mid, s, e), get_max((p<<1)|1, mid+1, r, s, e))
ans = 0
for x, j in sorted_seq:
i = -j
cur = get_max(1, 0, n-1, 0, i-1)+1
ans = max(ans, cur)
update(1, 0, n-1, i, cur)
return ans
```

- matrix_chain_order

```
'''
Dynamic Programming
Implementation of matrix Chain Multiplication
Time Complexity: O(n^3)
Space Complexity: O(n^2)
'''
INF = float("inf")
def matrix_chain_order(array):
n=len(array)
matrix = [[0 for x in range(n)] for x in range(n)]
sol = [[0 for x in range(n)] for x in range(n)]
for chain_length in range(2,n):
for a in range(1,n-chain_length+1):
b = a+chain_length-1
matrix[a][b] = INF
for c in range(a, b):
cost = matrix[a][c] + matrix[c+1][b] + array[a-1]*array[c]*array[b]
if cost < matrix[a][b]:
matrix[a][b] = cost
sol[a][b] = c
return matrix , sol
#Print order of matrix with Ai as matrix
def print_optimal_solution(optimal_solution,i,j):
if i==j:
print("A" + str(i),end = " ")
else:
print("(",end = " ")
print_optimal_solution(optimal_solution,i,optimal_solution[i][j])
print_optimal_solution(optimal_solution,optimal_solution[i][j]+1,j)
print(")",end = " ")
def main():
array=[30,35,15,5,10,20,25]
n=len(array)
#Size of matrix created from above array will be
# 30*35 35*15 15*5 5*10 10*20 20*25
matrix , optimal_solution = matrix_chain_order(array)
print("No. of Operation required: "+str((matrix[1][n-1])))
print_optimal_solution(optimal_solution,1,n-1)
if __name__ == '__main__':
main()
```

- max_product_subarray

```
"""
Find the contiguous subarray within an array
(containing at least one number) which has the largest product.
For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.
"""
from functools import reduce
def max_product(nums):
"""
:type nums: List[int]
:rtype: int
"""
lmin = lmax = gmax = nums[0]
for i in range(len(nums)):
t1 = nums[i] * lmax
t2 = nums[i] * lmin
lmax = max(max(t1, t2), nums[i])
lmin = min(min(t1, t2), nums[i])
gmax = max(gmax, lmax)
'''
Another approach that would print max product and the subarray
Examples:
subarray_with_max_product([2,3,6,-1,-1,9,5])
#=> max_product_so_far: 45, [-1, -1, 9, 5]
subarray_with_max_product([-2,-3,6,0,-7,-5])
#=> max_product_so_far: 36, [-2, -3, 6]
subarray_with_max_product([-4,-3,-2,-1])
#=> max_product_so_far: 24, [-4, -3, -2, -1]
subarray_with_max_product([-3,0,1])
#=> max_product_so_far: 1, [1]
'''
def subarray_with_max_product(arr):
''' arr is list of positive/negative numbers '''
l = len(arr)
product_so_far = max_product_end = 1
max_start_i = 0
so_far_start_i = so_far_end_i = 0
all_negative_flag = True
for i in range(l):
max_product_end *= arr[i]
if arr[i] > 0:
all_negative_flag = False
if max_product_end <= 0:
max_product_end = arr[i]
max_start_i = i
if product_so_far <= max_product_end:
product_so_far = max_product_end
so_far_end_i = i
so_far_start_i = max_start_i
if all_negative_flag:
print("max_product_so_far: %s, %s" %
(reduce(lambda x, y: x * y, arr), arr))
else:
print("max_product_so_far: %s, %s" %
(product_so_far, arr[so_far_start_i:so_far_end_i + 1]))
```

- max_subarray

```
def max_subarray(array):
max_so_far = max_now = array[0]
for i in range(1, len(array)):
max_now = max(array[i], max_now + array[i])
max_so_far = max(max_so_far, max_now)
return max_so_far
a = [1, 2, -3, 4, 5, -7, 23]
print(a)
print(max_subarray(a))
```

- min_cost_path

```
"""
author @goswami-rahul
To find minimum cost path
from station 0 to station N-1,
where cost of moving from ith station to jth station is given as:
Matrix of size (N x N)
where Matrix[i][j] denotes the cost of moving from
station i --> station j for i < j
NOTE that values where Matrix[i][j] and i > j does not
mean anything, and hence represented by -1 or INF
For the input below (cost matrix),
Minimum cost is obtained as from { 0 --> 1 --> 3}
= cost[0][1] + cost[1][3] = 65
the Output will be:
The Minimum cost to reach station 4 is 65
Time Complexity: O(n^2)
Space Complexity: O(n)
"""
INF = float("inf")
def min_cost(cost):
n = len(cost)
# dist[i] stores minimum cost from 0 --> i.
dist = [INF] * n
dist[0] = 0 # cost from 0 --> 0 is zero.
for i in range(n):
for j in range(i+1,n):
dist[j] = min(dist[j], dist[i] + cost[i][j])
return dist[n-1]
if __name__ == '__main__':
cost = [ [ 0, 15, 80, 90], # cost[i][j] is the cost of
[-1, 0, 40, 50], # going from i --> j
[-1, -1, 0, 70],
[-1, -1, -1, 0] ] # cost[i][j] = -1 for i > j
total_len = len(cost)
mcost = min_cost(cost)
assert mcost == 65
print("The Minimum cost to reach station %d is %d" % (total_len, mcost))
```

- num_decodings

```
"""
A message containing letters from A-Z is being
encoded to numbers using the following mapping:
'A' -> 1
'B' -> 2
...
'Z' -> 26
Given an encoded message containing digits,
determine the total number of ways to decode it.
For example,
Given encoded message "12",
it could be decoded as "AB" (1 2) or "L" (12).
The number of ways decoding "12" is 2.
"""
def num_decodings(s):
"""
:type s: str
:rtype: int
"""
if not s or s[0] == "0":
return 0
wo_last, wo_last_two = 1, 1
for i in range(1, len(s)):
x = wo_last if s[i] != "0" else 0
y = wo_last_two if int(s[i-1:i+1]) < 27 and s[i-1] != "0" else 0
wo_last_two = wo_last
wo_last = x+y
return wo_last
def num_decodings2(s):
if not s or s.startswith('0'):
return 0
stack = [1, 1]
for i in range(1, len(s)):
if s[i] == '0':
if s[i-1] == '0' or s[i-1] > '2':
# only '10', '20' is valid
return 0
stack.append(stack[-2])
elif 9 < int(s[i-1:i+1]) < 27:
# '01 - 09' is not allowed
stack.append(stack[-2]+stack[-1])
else:
# other case '01, 09, 27'
stack.append(stack[-1])
return stack[-1]
```

- regex_matching

```
"""
Implement regular expression matching with support for '.' and '*'.
'.' Matches any single character.
'*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
The function prototype should be:
bool is_match(const char *s, const char *p)
Some examples:
is_match("aa","a") → false
is_match("aa","aa") → true
is_match("aaa","aa") → false
is_match("aa", "a*") → true
is_match("aa", ".*") → true
is_match("ab", ".*") → true
is_match("aab", "c*a*b") → true
"""
import unittest
class Solution(object):
def is_match(self, s, p):
m, n = len(s) + 1, len(p) + 1
matches = [[False] * n for _ in range(m)]
# Match empty string with empty pattern
matches[0][0] = True
# Match empty string with .*
for i, element in enumerate(p[1:], 2):
matches[0][i] = matches[0][i - 2] and element == '*'
for i, ss in enumerate(s, 1):
for j, pp in enumerate(p, 1):
if pp != '*':
# The previous character has matched and the current one
# has to be matched. Two possible matches: the same or .
matches[i][j] = matches[i - 1][j - 1] and \
(ss == pp or pp == '.')
else:
# Horizontal look up [j - 2].
# Not use the character before *.
matches[i][j] |= matches[i][j - 2]
# Vertical look up [i - 1].
# Use at least one character before *.
# p a b *
# s 1 0 0 0
# a 0 1 0 1
# b 0 0 1 1
# b 0 0 0 ?
if ss == p[j - 2] or p[j - 2] == '.':
matches[i][j] |= matches[i - 1][j]
return matches[-1][-1]
class TestSolution(unittest.TestCase):
def test_none_0(self):
s = ""
p = ""
self.assertTrue(Solution().is_match(s, p))
def test_none_1(self):
s = ""
p = "a"
self.assertFalse(Solution().is_match(s, p))
def test_no_symbol_equal(self):
s = "abcd"
p = "abcd"
self.assertTrue(Solution().is_match(s, p))
def test_no_symbol_not_equal_0(self):
s = "abcd"
p = "efgh"
self.assertFalse(Solution().is_match(s, p))
def test_no_symbol_not_equal_1(self):
s = "ab"
p = "abb"
self.assertFalse(Solution().is_match(s, p))
def test_symbol_0(self):
s = ""
p = "a*"
self.assertTrue(Solution().is_match(s, p))
def test_symbol_1(self):
s = "a"
p = "ab*"
self.assertTrue(Solution().is_match(s, p))
def test_symbol_2(self):
# E.g.
# s a b b
# p 1 0 0 0
# a 0 1 0 0
# b 0 0 1 0
# * 0 1 1 1
s = "abb"
p = "ab*"
self.assertTrue(Solution().is_match(s, p))
if __name__ == "__main__":
unittest.main()
```

- rod_cut

```
# A Dynamic Programming solution for Rod cutting problem
INT_MIN = -32767
# Returns the best obtainable price for a rod of length n and
# price[] as prices of different pieces
def cut_rod(price):
n = len(price)
val = [0]*(n+1)
# Build the table val[] in bottom up manner and return
# the last entry from the table
for i in range(1, n+1):
max_val = INT_MIN
for j in range(i):
max_val = max(max_val, price[j] + val[i-j-1])
val[i] = max_val
return val[n]
# Driver program to test above functions
arr = [1, 5, 8, 9, 10, 17, 17, 20]
print("Maximum Obtainable Value is " + str(cut_rod(arr)))
# This code is contributed by Bhavya Jain
```

- word_break

```
"""
Given a non-empty string s and a dictionary wordDict
containing a list of non-empty words,
determine if s can be segmented into a space-separated
sequence of one or more dictionary words.
You may assume the dictionary does not contain duplicate words.
For example, given
s = "leetcode",
dict = ["leet", "code"].
Return true because "leetcode" can be segmented as "leet code".
"""
"""
s = abc word_dict = ["a","bc"]
True False False False
"""
# TC: O(N^2) SC: O(N)
def word_break(s, word_dict):
"""
:type s: str
:type word_dict: Set[str]
:rtype: bool
"""
dp = [False] * (len(s)+1)
dp[0] = True
for i in range(1, len(s)+1):
for j in range(0, i):
if dp[j] and s[j:i] in word_dict:
dp[i] = True
break
return dp[-1]
if __name__ == "__main__":
s = "keonkim"
dic = ["keon", "kim"]
print(word_break(s, dic))
```

- fibonacci

```
'''
In mathematics, the Fibonacci numbers, commonly denoted Fn, form a sequence, called the Fibonacci sequence,
such that each number is the sum of the two preceding ones, starting from 0 and 1.
That is,
F0=0 , F1=1
and
Fn= F(n-1) + F(n-2)
The Fibonacci numbers are the numbers in the following integer sequence.
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …….
In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation
Here, given a number n, print n-th Fibonacci Number.
'''
def fib_recursive(n):
"""[summary]
Computes the n-th fibonacci number recursive.
Problem: This implementation is very slow.
approximate O(2^n)
Arguments:
n {[int]} -- [description]
Returns:
[int] -- [description]
"""
# precondition
assert n >= 0, 'n must be a positive integer'
if n <= 1:
return n
else:
return fib_recursive(n-1) + fib_recursive(n-2)
# print(fib_recursive(35)) # => 9227465 (slow)
def fib_list(n):
"""[summary]
This algorithm computes the n-th fibbonacci number
very quick. approximate O(n)
The algorithm use dynamic programming.
Arguments:
n {[int]} -- [description]
Returns:
[int] -- [description]
"""
# precondition
assert n >= 0, 'n must be a positive integer'
list_results = [0, 1]
for i in range(2, n+1):
list_results.append(list_results[i-1] + list_results[i-2])
return list_results[n]
# print(fib_list(100)) # => 354224848179261915075
def fib_iter(n):
"""[summary]
Works iterative approximate O(n)
Arguments:
n {[int]} -- [description]
Returns:
[int] -- [description]
"""
# precondition
assert n >= 0, 'n must be positive integer'
fib_1 = 0
fib_2 = 1
sum = 0
if n <= 1:
return n
for _ in range(n-1):
sum = fib_1 + fib_2
fib_1 = fib_2
fib_2 = sum
return sum
# print(fib_iter(100)) # => 354224848179261915075
```

- hosoya triangle

```
"""
Hosoya triangle (originally Fibonacci triangle) is a triangular arrangement
of numbers, where if you take any number it is the sum of 2 numbers above.
First line is always 1, and second line is always {1 1}.
This printHosoya function takes argument n which is the height of the triangle
(number of lines).
For example:
printHosoya( 6 ) would return:
1
1 1
2 1 2
3 2 2 3
5 3 4 3 5
8 5 6 6 5 8
The complexity is O(n^3).
"""
def hosoya(n, m):
if ((n == 0 and m == 0) or (n == 1 and m == 0) or
(n == 1 and m == 1) or (n == 2 and m == 1)):
return 1
if n > m:
return hosoya(n - 1, m) + hosoya(n - 2, m)
elif m == n:
return hosoya(n - 1, m - 1) + hosoya(n - 2, m - 2)
else:
return 0
def print_hosoya(n):
for i in range(n):
for j in range(i + 1):
print(hosoya(i, j) , end = " ")
print ("\n", end = "")
def hosoya_testing(n):
x = []
for i in range(n):
for j in range(i + 1):
x.append(hosoya(i, j))
return x
```

- K-Factor_strings

```
"""
Hosoya triangle (originally Fibonacci triangle) is a triangular arrangement
of numbers, where if you take any number it is the sum of 2 numbers above.
First line is always 1, and second line is always {1 1}.
This printHosoya function takes argument n which is the height of the triangle
(number of lines).
For example:
printHosoya( 6 ) would return:
1
1 1
2 1 2
3 2 2 3
5 3 4 3 5
8 5 6 6 5 8
The complexity is O(n^3).
"""
def hosoya(n, m):
if ((n == 0 and m == 0) or (n == 1 and m == 0) or
(n == 1 and m == 1) or (n == 2 and m == 1)):
return 1
if n > m:
return hosoya(n - 1, m) + hosoya(n - 2, m)
elif m == n:
return hosoya(n - 1, m - 1) + hosoya(n - 2, m - 2)
else:
return 0
def print_hosoya(n):
for i in range(n):
for j in range(i + 1):
print(hosoya(i, j) , end = " ")
print ("\n", end = "")
def hosoya_testing(n):
x = []
for i in range(n):
for j in range(i + 1):
x.append(hosoya(i, j))
return x
```

- planting_trees

```
"""
An even number of trees are left along one side of a country road. You've been assigned the job to
plant these trees at an even interval on both sides of the road. The length L and width W of the road
are variable, and a pair of trees must be planted at the beginning (at 0) and at the end (at L) of
the road. Only one tree can be moved at a time. The goal is to calculate the lowest amount of
distance that the trees have to be moved before they are all in a valid position.
"""
from math import sqrt
import sys
def planting_trees(trees, L, W):
"""
Returns the minimum distance that trees have to be moved before they are all in a valid state.
Parameters:
tree (list<int>): A sorted list of integers with all trees' position along the road.
L (int): An integer with the length of the road.
W (int): An integer with the width of the road.
Returns:
A float number with the total distance trees have been moved.
"""
trees = [0] + trees
n_pairs = int(len(trees)/2)
space_between_pairs = L/(n_pairs-1)
target_locations = [location*space_between_pairs for location in range(n_pairs)]
cmatrix = [[0 for _ in range(n_pairs+1)] for _ in range(n_pairs+1)]
for ri in range(1, n_pairs+1):
cmatrix[ri][0] = cmatrix[ri-1][0] + sqrt(W + abs(trees[ri]-target_locations[ri-1])**2)
for li in range(1, n_pairs+1):
cmatrix[0][li] = cmatrix[0][li-1] + abs(trees[li]-target_locations[li-1])
for ri in range(1, n_pairs+1):
for li in range(1, n_pairs+1):
cmatrix[ri][li] = min(
cmatrix[ri-1][li] + sqrt(W + (trees[li + ri]-target_locations[ri-1])**2),
cmatrix[ri][li-1] + abs(trees[li + ri]-target_locations[li-1])
)
return cmatrix[n_pairs][n_pairs]
```

- graph
- check_bipartite

```
"""
Bipartite graph is a graph whose vertices can be divided into two disjoint and independent sets.
(https://en.wikipedia.org/wiki/Bipartite_graph)
Time complexity is O(|E|)
Space complexity is O(|V|)
"""
def check_bipartite(adj_list):
V = len(adj_list)
# Divide vertexes in the graph into set_type 1 and 2
# Initialize all set_types as -1
set_type = [-1 for v in range(V)]
set_type[0] = 0
q = [0]
while q:
v = q.pop(0)
# If there is a self-loop, it cannot be bipartite
if adj_list[v][v]:
return False
for u in range(V):
if adj_list[v][u]:
if set_type[u] == set_type[v]:
return False
elif set_type[u] == -1:
# set type of u opposite of v
set_type[u] = 1 - set_type[v]
q.append(u)
return True
```

- strongly_connected

```
from collections import defaultdict
class Graph:
def __init__(self,v):
self.v = v
self.graph = defaultdict(list)
def add_edge(self,u,v):
self.graph[u].append(v)
def dfs(self):
visited = [False] * self.v
self.dfs_util(0,visited)
if visited == [True]*self.v:
return True
return False
def dfs_util(self,i,visited):
visited[i] = True
for u in self.graph[i]:
if not(visited[u]):
self.dfs_util(u,visited)
def reverse_graph(self):
g = Graph(self.v)
for i in range(len(self.graph)):
for j in self.graph[i]:
g.add_edge(j,i)
return g
def is_sc(self):
if self.dfs():
gr = self.reverse_graph()
if gr.dfs():
return True
return False
g1 = Graph(5)
g1.add_edge(0, 1)
g1.add_edge(1, 2)
g1.add_edge(2, 3)
g1.add_edge(3, 0)
g1.add_edge(2, 4)
g1.add_edge(4, 2)
print ("Yes") if g1.is_sc() else print("No")
g2 = Graph(4)
g2.add_edge(0, 1)
g2.add_edge(1, 2)
g2.add_edge(2, 3)
print ("Yes") if g2.is_sc() else print("No")
```

- clone_graph

```
"""
Clone an undirected graph. Each node in the graph contains a label and a list
of its neighbors.
OJ's undirected graph serialization:
Nodes are labeled uniquely.
We use # as a separator for each node, and , as a separator for node label and
each neighbor of the node.
As an example, consider the serialized graph {0,1,2#1,2#2,2}.
The graph has a total of three nodes, and therefore contains three parts as
separated by #.
First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
Second node is labeled as 1. Connect node 1 to node 2.
Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a
self-cycle.
Visually, the graph looks like the following:
1
/ \
/ \
0 --- 2
/ \
\_/
"""
import collections
# Definition for a undirected graph node
class UndirectedGraphNode:
def __init__(self, x):
self.label = x
self.neighbors = []
# BFS
def clone_graph1(node):
if not node:
return
node_copy = UndirectedGraphNode(node.label)
dic = {node: node_copy}
queue = collections.deque([node])
while queue:
node = queue.popleft()
for neighbor in node.neighbors:
if neighbor not in dic: # neighbor is not visited
neighbor_copy = UndirectedGraphNode(neighbor.label)
dic[neighbor] = neighbor_copy
dic[node].neighbors.append(neighbor_copy)
queue.append(neighbor)
else:
dic[node].neighbors.append(dic[neighbor])
return node_copy
# DFS iteratively
def clone_graph2(node):
if not node:
return
node_copy = UndirectedGraphNode(node.label)
dic = {node: node_copy}
stack = [node]
while stack:
node = stack.pop()
for neighbor in node.neighbors:
if neighbor not in dic:
neighbor_copy = UndirectedGraphNode(neighbor.label)
dic[neighbor] = neighbor_copy
dic[node].neighbors.append(neighbor_copy)
stack.append(neighbor)
else:
dic[node].neighbors.append(dic[neighbor])
return node_copy
# DFS recursively
def clone_graph(node):
if not node:
return
node_copy = UndirectedGraphNode(node.label)
dic = {node: node_copy}
dfs(node, dic)
return node_copy
def dfs(node, dic):
for neighbor in node.neighbors:
if neighbor not in dic:
neighbor_copy = UndirectedGraphNode(neighbor.label)
dic[neighbor] = neighbor_copy
dic[node].neighbors.append(neighbor_copy)
dfs(neighbor, dic)
else:
dic[node].neighbors.append(dic[neighbor])
```

- cycle_detection

```
"""
Given a directed graph, check whether it contains a cycle.
Real-life scenario: deadlock detection in a system. Processes may be
represented by vertices, then and an edge A -> B could mean that process A is
waiting for B to release its lock on a resource.
"""
from enum import Enum
class TraversalState(Enum):
WHITE = 0
GRAY = 1
BLACK = 2
example_graph_with_cycle = {'A': ['B', 'C'],
'B': ['D'],
'C': ['F'],
'D': ['E', 'F'],
'E': ['B'],
'F': []}
example_graph_without_cycle = {'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F'],
'D': ['E'],
'E': [],
'F': []}
def is_in_cycle(graph, traversal_states, vertex):
if traversal_states[vertex] == TraversalState.GRAY:
return True
traversal_states[vertex] = TraversalState.GRAY
for neighbor in graph[vertex]:
if is_in_cycle(graph, traversal_states, neighbor):
return True
traversal_states[vertex] = TraversalState.BLACK
return False
def contains_cycle(graph):
traversal_states = {vertex: TraversalState.WHITE for vertex in graph}
for vertex, state in traversal_states.items():
if (state == TraversalState.WHITE and
is_in_cycle(graph, traversal_states, vertex)):
return True
return False
print(contains_cycle(example_graph_with_cycle))
print(contains_cycle(example_graph_without_cycle))
```

- find_all_cliques

```
# takes dict of sets
# each key is a vertex
# value is set of all edges connected to vertex
# returns list of lists (each sub list is a maximal clique)
# implementation of the basic algorithm described in:
# Bron, Coen; Kerbosch, Joep (1973), "Algorithm 457: finding all cliques of an undirected graph",
def find_all_cliques(edges):
def expand_clique(candidates, nays):
nonlocal compsub
if not candidates and not nays:
nonlocal solutions
solutions.append(compsub.copy())
else:
for selected in candidates.copy():
candidates.remove(selected)
candidates_temp = get_connected(selected, candidates)
nays_temp = get_connected(selected, nays)
compsub.append(selected)
expand_clique(candidates_temp, nays_temp)
nays.add(compsub.pop())
def get_connected(vertex, old_set):
new_set = set()
for neighbor in edges[str(vertex)]:
if neighbor in old_set:
new_set.add(neighbor)
return new_set
compsub = []
solutions = []
possibles = set(edges.keys())
expand_clique(possibles, set())
return solutions
```

- find_path

```
myGraph = {'A': ['B', 'C'],
'B': ['C', 'D'],
'C': ['D', 'F'],
'D': ['C'],
'E': ['F'],
'F': ['C']}
# find path from start to end using recursion with backtracking
def find_path(graph, start, end, path=[]):
path = path + [start]
if (start == end):
return path
if not start in graph:
return None
for node in graph[start]:
if node not in path:
newpath = find_path(graph, node, end, path)
return newpath
return None
# find all path
def find_all_path(graph, start, end, path=[]):
path = path + [start]
if (start == end):
return [path]
if not start in graph:
return None
paths = []
for node in graph[start]:
if node not in path:
newpaths = find_all_path(graph, node, end, path)
for newpath in newpaths:
paths.append(newpath)
return paths
def find_shortest_path(graph, start, end, path=[]):
path = path + [start]
if start == end:
return path
if start not in graph:
return None
shortest = None
for node in graph[start]:
if node not in path:
newpath = find_shortest_path(graph, node, end, path)
if newpath:
if not shortest or len(newpath) < len(shortest):
shortest = newpath
return shortest
print(find_all_path(myGraph, 'A', 'F'))
# print(find_shortest_path(myGraph, 'A', 'D'))
```

- graph

```
"""
These are classes to represent a Graph and its elements.
It can be shared across graph algorithms.
"""
class Node(object):
def __init__(self, name):
self.name = name
@staticmethod
def get_name(obj):
if isinstance(obj, Node):
return obj.name
elif isinstance(obj, str):
return obj
return''
def __eq__(self, obj):
return self.name == self.get_name(obj)
def __repr__(self):
return self.name
def __hash__(self):
return hash(self.name)
def __ne__(self, obj):
return self.name != self.get_name(obj)
def __lt__(self, obj):
return self.name < self.get_name(obj)
def __le__(self, obj):
return self.name <= self.get_name(obj)
def __gt__(self, obj):
return self.name > self.get_name(obj)
def __ge__(self, obj):
return self.name >= self.get_name(obj)
def __bool__(self):
return self.name
class DirectedEdge(object):
def __init__(self, node_from, node_to):
self.nf = node_from
self.nt = node_to
def __eq__(self, obj):
if isinstance(obj, DirectedEdge):
return obj.nf == self.nf and obj.nt == self.nt
return False
def __repr__(self):
return '({0} -> {1})'.format(self.nf, self.nt)
class DirectedGraph(object):
def __init__(self, load_dict={}):
self.nodes = []
self.edges = []
self.adjmt = {}
if load_dict and type(load_dict) == dict:
for v in load_dict:
node_from = self.add_node(v)
self.adjmt[node_from] = []
for w in load_dict[v]:
node_to = self.add_node(w)
self.adjmt[node_from].append(node_to)
self.add_edge(v, w)
def add_node(self, node_name):
try:
return self.nodes[self.nodes.index(node_name)]
except ValueError:
node = Node(node_name)
self.nodes.append(node)
return node
def add_edge(self, node_name_from, node_name_to):
try:
node_from = self.nodes[self.nodes.index(node_name_from)]
node_to = self.nodes[self.nodes.index(node_name_to)]
self.edges.append(DirectedEdge(node_from, node_to))
except ValueError:
pass
class Graph:
def __init__(self, vertices):
# No. of vertices
self.V = vertices
# default dictionary to store graph
self.graph = {}
# To store transitive closure
self.tc = [[0 for j in range(self.V)] for i in range(self.V)]
# function to add an edge to graph
def add_edge(self, u, v):
if u in self.graph:
self.graph[u].append(v)
else:
self.graph[u] = [v]
#g = Graph(4)
#g.add_edge(0, 1)
#g.add_edge(0, 2)
#g.add_edge(1, 2)
#g.add_edge(2, 0)
#g.add_edge(2, 3)
#g.add_edge(3, 3)
```

- dijkstra

```
#Dijkstra's single source shortest path algorithm
class Dijkstra():
def __init__(self, vertices):
self.vertices = vertices
self.graph = [[0 for column in range(vertices)] for row in range(vertices)]
def min_distance(self, dist, min_dist_set):
min_dist = float("inf")
for v in range(self.vertices):
if dist[v] < min_dist and min_dist_set[v] == False:
min_dist = dist[v]
min_index = v
return min_index
def dijkstra(self, src):
dist = [float("inf")] * self.vertices
dist[src] = 0
min_dist_set = [False] * self.vertices
for count in range(self.vertices):
#minimum distance vertex that is not processed
u = self.min_distance(dist, min_dist_set)
#put minimum distance vertex in shortest tree
min_dist_set[u] = True
#Update dist value of the adjacent vertices
for v in range(self.vertices):
if self.graph[u][v] > 0 and min_dist_set[v] == False and dist[v] > dist[u] + self.graph[u][v]:
dist[v] = dist[u] + self.graph[u][v]
return dist
```

- markov_chain

```
import random
my_chain = {
'A': {'A': 0.6,
'E': 0.4},
'E': {'A': 0.7,
'E': 0.3}
}
def __choose_state(state_map):
choice = random.random()
probability_reached = 0
for state, probability in state_map.items():
probability_reached += probability
if probability_reached > choice:
return state
def next_state(chain, current_state):
next_state_map = chain.get(current_state)
next_state = __choose_state(next_state_map)
return next_state
def iterating_markov_chain(chain, state):
while True:
state = next_state(chain, state)
yield state
```

- minimum_spanning_tree
- satisfiability
- minimum_spanning_tree_prims
- tarjan
- traversal
- maximum_flow_bfs
- maximum_flow_dfs
- all_pairs_shortest_path
- bellman_ford
- Count Connected Components
- heap
- linkedlist
- map
- maths
- base_conversion
- chinese_remainder_theorem
- combination
- cosine_similarity
- decimal_to_binary_ip
- diffie_hellman_key_exchange
- euler_totient
- extended_gcd
- factorial
- find_order
- find_primitive_root
- gcd/lcm
- generate_strobogrammtic
- hailstone
- is_strobogrammatic
- krishnamurthy_number
- magic_number
- modular_exponential
- modular_inverse
- next_bigger
- next_perfect_square
- nth_digit
- polynomial
- power
- prime_check
- primes_sieve_of_eratosthenes
- pythagoras
- rabin_miller
- recursive_binomial_coefficient
- rsa
- sqrt_precision_factor
- summing_digits
- symmetry_group_cycle_index

- matrix
- queues
- search
- set
- sort
- stack
- streaming
- strings
- fizzbuzz
- delete_reoccurring
- strip_url_params
- validate_coordinates
- domain_extractor
- merge_string_checker
- add_binary
- breaking_bad
- decode_string
- encode_decode
- group_anagrams
- int_to_roman
- is_palindrome
- license_number
- make_sentence
- multiply_strings
- one_edit_distance
- rabin_karp
- reverse_string
- reverse_vowel
- reverse_words
- roman_to_int
- word_squares
- unique_morse
- judge_circle
- strong_password
- caesar_cipher
- check_pangram
- contain_string
- count_binary_substring
- repeat_string
- min_distance
- longest_common_prefix
- rotate
- first_unique_char
- repeat_substring
- longest_palindromic_substring
- panagram

- tree
- bst
- [fenwick_tree](algorithms/tree/fenwick_tree]
- fenwick_tree
- red_black_tree
- segment_tree
- traversal
- trie
- b_tree
- binary_tree_paths
- bin_tree_to_list
- construct_tree_preorder_postorder
- deepest_left
- invert_tree
- is_balanced
- is_subtree
- is_symmetric
- longest_consecutive
- lowest_common_ancestor
- max_height
- max_path_sum
- min_height
- path_sum
- path_sum2
- pretty_print
- same_tree
- tree

- unix
- unionfind

**Download Details: **

Author: keon

Source Code: https://github.com/keon/algorithms

License: MIT

#datastructures #algorithms #python

1626341412

**AppClues Infotech** is a high-end mobile app development company **creating AR/VR/MR, Mobile and Web applications** for digital agencies, startups, and enterprises. We have 120+ dedicated designers & developers teams who can help to develop a perfect mobile & web application for your specific business needs.

If you have any** mobile and web app development** project ideas kindly share your requirement with us.

**Why Choose AppClues Infotech For AR/VR/MR App Development?**

- Strong Technical Skill
- Vast Experience
- Agile Methodology
- Creative and Flexible Solutions
- Customer-centric Approach
- Seamless Communication
- Competitive Price
- 100 % customer satisfaction
- 24/7 Technical Support

**For more info:
Website: https://www.appcluesinfotech.com/
Email: info@appcluesinfotech.com
Call: +1-978-309-9910**

#augmented reality app development usa #virtual reality app development usa #mixed reality app development usa #hire augmented reality app developers #hire virtual reality app developers #hire mixed reality app developers

1623940680

Snake game mixed with Conway’s Game of Life

The rules are the same than a normal snake game but you have to avoid cells created by Conway’s Game of Life. New pattern are added every 3, 2 or 1 food eaten (depending the chosen level). However, if one of these cell is in contact with food it turns to food.

- numpy
- pygame

You can pass the level (1 to 3) as argument, by default it will be 1.

```
python snakeoflife.py 3
```

…

#games #snake game mixed with conway's game of life for python #snake game mixed #conway's game of life #python #conway's

1664775764

How to create a game app is a comprehensive guide, explaining the entire process of creating and publishing games for iOS and Android. Covering all the essential information a budding game developer needs to know.

Read More - https://www.brsoftech.com/blog/how-to-create-a-game-app/

1595573880

In this post, we will investigate how easily we can train a Deep Q-Network (DQN) agent (Mnih et al., 2015) for Atari 2600 games using the Google reinforcement learning library Dopamine. While many RL libraries exist, this library is specifically designed with **four essential features** in mind:

- Easy experimentation
- Flexible development
- Compact and reliable
- Reproducible

_We believe these principles makes _

_Dopamine _one of the. Additionally, we even got the library to work on Windows, which we think isbest RL learning environment available today!quite a feat

In my view, the visualization of any trained RL agent is an **absolute must** in reinforcement learning! Therefore, we will (of course) include this for our own trained agent at the very end!

We will go through all the pieces of code required (which is** minimal compared to other libraries**), but you can also find all scripts needed in the following Github repo.

The general premise of deep reinforcement learning is to

“derive efficient representations of the environment from high-dimensional sensory inputs, and use these to generalize past experience to new situations.”

- Mnih et al. (2015)

As stated earlier, we will implement the *DQN model* by *Deepmind*, which only uses raw pixels and game score as input. The raw pixels are processed using convolutional neural networks similar to image classification. The primary difference lies in the **objective function**, which for the DQN agent is called the *optimal action-value function*

where_ rₜ *is the maximum sum of rewards at time t discounted by γ, obtained using a behavior policy* π = P(a_∣_s)_ for each observation-action pair.

There are relatively many details to Deep Q-Learning, such as *Experience Replay* (Lin, 1993) and an _iterative update rule. _Thus, we refer the reader to the original paper for an excellent walk-through of the mathematical details.

One key benefit of DQN compared to previous approaches at the time (2015) was the ability to outperform existing methods for Atari 2600 games using the **same set of hyperparameters** and **only pixel values and game score as input**, clearly a tremendous achievement.

This post does not include instructions for installing Tensorflow, but we do want to stress that you can use **both the CPU and GPU versions**.

Nevertheless, assuming you are using `Python 3.7.x`

, these are the libraries you need to install (which can all be installed via `pip`

):

```
tensorflow-gpu=1.15 (or tensorflow==1.15 for CPU version)
cmake
dopamine-rl
atari-py
matplotlib
pygame
seaborn
pandas
```

#reinforcement-learning #q-learning #games #machine-learning #deep-learning #deep learning