Zara  Bryant

Zara Bryant

1611712081

Exploring Mixed Reality in Games| Learn with Dr G

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

What is GEEK

Buddha Community

Exploring Mixed Reality in Games| Learn with Dr G
Zak Dyer

Zak Dyer

1644477426

Minimal Examples of Data Structures and Algorithms in Python

Pythonic Data Structures and Algorithms

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

Tests

Use unittest

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

Use pytest

For running all tests write down:

$ python3 -m pytest tests

Install

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)

Uninstall

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

$ pip3 uninstall -y algorithms

List of Implementations

  • 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
"""
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))
"""
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)
"""
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)
"""
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]
"""

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

Download Details: 
Author: keon
Source Code: https://github.com/keon/algorithms 
License: MIT
#datastructures #algorithms #python

 

Augmented Reality | Virtual Reality | Mixed Reality - Mobile & Web App Development

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

Ray  Patel

Ray Patel

1623940680

Snake game mixed with Conway's Game of Life for python

SnakeOfLife

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.

Requirements

  • numpy
  • pygame

Usage

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

lakshya world

lakshya world

1664775764

How to Create a Successful Gaming App?

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/

 

#game-engine  #game-development  #game  #games  #gaming 

Tia  Gottlieb

Tia Gottlieb

1595573880

Deep Reinforcement Learning for Video Games Made Easy

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 best RL learning environment available today. Additionally, we even got the library to work on Windows, which we think is 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.

1. Brief Introduction to Reinforcement Learning and Deep Q-Learning

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

Image for post

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.

2. Installation

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