├── 1_recursive_algorithms ├── 2_data_structures ├── 3_divide_conquer ├── 4_greedy_algorithms ├── 5_dynamic_programming └── README.md /1_recursive_algorithms: -------------------------------------------------------------------------------- 1 | Recursion, which originates from the Latin verb, “recurrere” means to “run back”... 2 | and this is what the program we’re going to write together is going to do- run back to itself, over and over, 3 | as many times as it needs to until the program terminates. 4 | 5 | 6 | ____________________________________________________EXAMPLES__________________________________________________________ 7 | 8 | FACTORIALS 9 | 10 | def iterative_factorial(n): 11 | fact = 1 12 | for i in range(2, n + 1): 13 | fact *= i 14 | return fact 15 | #print(iterative_factorial(5)) 16 | 17 | def recur_factorial(n): 18 | if n == 1: 19 | return n 20 | else: 21 | temp = recur_factorial(n-1) 22 | return temp * n 23 | #print(recur_factorial(5)) 24 | 25 | # Two-line recursive program to calculate factorial 26 | def recur_factorial(n): 27 | if n == 1: return n 28 | else: return n * recur_factorial(n-1) 29 | #print(recur_factorial(5)) 30 | 31 | _______________________________________________________________ 32 | 33 | 34 | PERMUTATIONS 35 | 36 | # ITERATIVE PROGRAM FOR PERMUTATIONS 37 | 38 | from math import factorial 39 | 40 | def permutations(str): 41 | for p in range(factorial(len(str))): 42 | print(''.join(str)) 43 | i = len(str) - 1 44 | while i > 0 and str[i - 1] > str[i]: 45 | i -= 1 46 | str[i:] = reversed(str[i:]) 47 | if i > 0: 48 | q = i 49 | while str[i - 1] > str[q]: 50 | q += 1 51 | temp = str[i - 1] 52 | str[i - 1] = str[q] 53 | str[q] = temp 54 | s = 'abc' 55 | s = list(s) 56 | permutations(s) 57 | 58 | 59 | # RECURSIVE PROGRAM FOR PERMUTATIONS 60 | 61 | def permute(string, pocket=""): 62 | if len(string) == 0: 63 | print(pocket) 64 | else: 65 | for i in range((len(string))): 66 | letter = string[i] 67 | front = string[0:i] 68 | back = string[i+1:] 69 | together = front + back 70 | permute(together, letter + pocket) 71 | #print(permute("ABC", "")) 72 | 73 | _______________________________________________________________ 74 | 75 | 76 | NO PROGRAM WAS DISCUSSED FOR N-QUEENS PROBLEM 77 | 78 | -------------------------------------------------------------------------------- /2_data_structures: -------------------------------------------------------------------------------- 1 | For the basic section, we covered- Linear Arrays: 2 | Linear Search 3 | Binary Search 4 | Bubble Sort 5 | Insertion Sort 6 | 7 | Advanced- Linked Lists: 8 | Traverse 9 | Search 10 | Insert 11 | Delete 12 | 13 | 14 | Mindbreaker- Hash Tables: 15 | Associative arrays 16 | Hash functions 17 | Keys-value pairs 18 | Collision, and 19 | Chaining 20 | 21 | _____________________________________________________________EXAMPLES____________________________________________________ 22 | 23 | 24 | BASIC - SEARCH & SORT: 25 | 26 | LINEAR SEARCH 27 | 28 | def search(arr, target): 29 | for i in range(len(arr)): 30 | 31 | if arr[i] == target: 32 | return i 33 | 34 | return -1 35 | 36 | arr = [2, 5, 8, 10, 16, 22, 25] 37 | target = 16 38 | result = search(arr, target) 39 | 40 | if result != -1: 41 | print("Element is present at index", str(result)) 42 | else: 43 | print("Element is not present in array") 44 | 45 | 46 | 47 | ITERATIVE BINARY SEARCH 48 | 49 | def binary_itr(arr, start, end, target): 50 | while start <= end: 51 | 52 | mid = (start + end) // 2 53 | 54 | if arr[mid] < target: 55 | start = mid + 1 56 | 57 | elif arr[mid] > target: 58 | end = mid - 1 59 | 60 | else: 61 | return mid 62 | 63 | return start 64 | #return -1 65 | 66 | arr = [2, 5, 8, 10, 16, 22, 25] 67 | target = 25 68 | 69 | result = binary_itr(arr, 0, len(arr) - 1, target) 70 | #print(result) 71 | 72 | if result != -1: 73 | print("Element is present at index %d" % result) 74 | else: 75 | print("Element is not present in array") 76 | 77 | 78 | RECURSIVE BINARY SEARCH 79 | 80 | def binary_recur(arr, start, end, target): 81 | if end >= start: 82 | 83 | mid = start + end - 1 // 2 84 | 85 | if arr[mid] < target: 86 | binary_recur(arr, mid + 1, end, target) 87 | 88 | elif arr[mid] > target: 89 | return binary_recur(arr, start, mid - 1, target) 90 | else: 91 | return mid 92 | else: 93 | return -1 94 | 95 | arr = [2, 5, 8, 10, 16, 22, 25] 96 | target = 10 97 | result = binary_recur(arr, 0, len(arr) - 1, target) 98 | 99 | if result != -1: 100 | print("Element is present at index", str(result)) 101 | else: 102 | print("Element is not present in array") 103 | 104 | 105 | 106 | BUBBLE SORT - OPTIMIZED 107 | 108 | 109 | A = [9, 8, 7, 6, 5, 4, 3, 2, 1] 110 | 111 | def bubble_optimized(A): 112 | iterations = 0 113 | for i in range(len(A)): 114 | for j in range(len(A)-i-1): 115 | iterations += 1 116 | if A[j] > A[j+1]: 117 | A[j], A[j+1] = A[j+1], A[j] 118 | return A, iterations 119 | print(bubble_optimized(A)) 120 | 121 | 122 | BUBBLE SORT - UNOPTIMIZED 123 | 124 | def swap(A, i, j): 125 | temp = A[i] 126 | A[i] = A[j] 127 | A[j] = temp 128 | 129 | def bubble_sort_un_op(A): 130 | iterations = 0 131 | 132 | for i in A: 133 | for j in range(len(A)-1): 134 | iterations += 1 135 | if A[j] > A[j+1]: 136 | swap(A, j, j + 1) 137 | return A, iterations 138 | 139 | print(bubble_sort_un_op(A)) 140 | 141 | 142 | INSERTION SORT - SHIFTING ELEMENTS 143 | 144 | def insert_sort(A): 145 | for j in range(1, len(A)): 146 | key = A[j] 147 | i = j - 1 148 | while i >= 0 and A[i] > key: 149 | A[i + 1] = A[i] 150 | i -= 1 151 | A[i + 1] = key 152 | return A 153 | 154 | A = [5, 2, 4, 6, 1, 3] 155 | print(insert_sort(A)) 156 | 157 | 158 | 159 | INSERTION SORT - SWAPPING ELEMENTS 160 | 161 | def swap(A): 162 | for i in range(1, len(A)): 163 | for j in range(i-1, -1, -1): 164 | if A[j] > A[j+1]: 165 | A[j], A[j+1] = A[j+1], A[j] 166 | else: 167 | break 168 | return A 169 | print(swap(A)) 170 | 171 | __________________________________________________________ 172 | 173 | 174 | ADVANCED- LINKED LISTS: 175 | 176 | 177 | class Node: 178 | def __init__(self, data): 179 | self.data = data 180 | self.next = None 181 | 182 | class LinkedList: 183 | 184 | def traversal(self): 185 | first = self.head 186 | while first: 187 | print(first.data) 188 | first = first.next 189 | 190 | def insert_new_header(self, new_data): 191 | new_node = Node(new_data) 192 | new_node.next = self.head 193 | self.head = new_node 194 | 195 | def search(self, x): 196 | temp = self.head 197 | while temp is not None: 198 | if temp.data == x: 199 | return True 200 | temp = temp.next 201 | else: 202 | return False 203 | 204 | def delete_node(self, data): 205 | temp = self.head 206 | while temp is not None: 207 | if temp.data == data: 208 | break 209 | prev = temp 210 | temp = temp.next 211 | prev.next = temp.next 212 | 213 | def delete_tail(self): 214 | temp = self.head 215 | while temp.next.next is not None: 216 | temp = temp.next 217 | temp.next = None 218 | 219 | 220 | family = LinkedList() 221 | family.head = Node("Bob") 222 | wife = Node("Amy") 223 | first_kid = Node("Max") 224 | second_kid = Node("Jenny") 225 | 226 | family.head.next = wife 227 | wife.next = first_kid 228 | first_kid.next = second_kid 229 | 230 | family.insert_new_header("Dave") 231 | 232 | #family.delete_tail() 233 | 234 | #print(family.search("Bob")) 235 | family.delete_node("Amy") 236 | family.traversal() 237 | 238 | _________________________________________________________ 239 | 240 | 241 | MINDBREAKER- HASH TABLES 242 | 243 | NO CODE WAS DISCUSSED FOR HASH TABLES 244 | -------------------------------------------------------------------------------- /3_divide_conquer: -------------------------------------------------------------------------------- 1 | A divide-and-conquer algorithm paradigm can solve a large problem by recursively breaking it down into smaller 2 | subproblems until they become simple enough to be solved directly. 3 | 4 | Basic- Merge Sort 5 | Advanced- Matrix Multiplication 6 | Mindbreaker- Strassen Algorithm 7 | 8 | ____________________________________________EXAMPLES________________________________________________________ 9 | 10 | 11 | BASIC: MERGE SORT 12 | 13 | # 1. SORTING ITERATIVELY WITHOUT USING PYTHON'S BUILT IN SORTED FEATURE 14 | A = [-5, -23, 5, 0, 23, -6, 23, 67] 15 | C = [] 16 | while A: 17 | minimum = A[0] 18 | for x in A: 19 | if x < minimum: 20 | minimum = x 21 | C.append(minimum) 22 | A.remove(minimum) 23 | print(C) 24 | 25 | 26 | # 2. EXPLAINING THE CONCEPT - BASIC (MERGING TWO LISTS - DIVIDING INTO TWO, CONQUERING EACH, MERGING BACK) 27 | def merging(left, right): 28 | C = [] 29 | while min(len(left), len(right)) > 0: 30 | if left[0] > right[0]: 31 | insert = right.pop(0) 32 | C.append(insert) 33 | elif left[0] <= right[0]: 34 | insert = left.pop(0) 35 | C.append(insert) 36 | if len(left) > 0: 37 | for i in left: 38 | C.append(i) 39 | if len(right) > 0: 40 | for i in right: 41 | C.append(i) 42 | return C 43 | 44 | left = [2, 5, 6, 10] 45 | right = [3, 4, 12, 20] 46 | print(merging(left, right)) 47 | 48 | 49 | # 3. SORTING RECURSIVELY - TOP DOWN 50 | def sortArray(A): 51 | if len(A) <= 1: 52 | return A 53 | middle = len(A) // 2 54 | left = sortArray(A[:middle]) 55 | right = sortArray(A[middle:]) 56 | merged = [] 57 | while left and right: 58 | if left[0] <= right [0]: 59 | merged.append(left.pop(0)) 60 | else: 61 | merged.append(right.pop(0)) 62 | merged.extend(right if right else left) 63 | return merged 64 | print(sortArray(A)) 65 | 66 | 67 | # 4. SORTING ITERATIVELY WITHOUT USING PYTHON'S BUILT IN SORTED FEATURE - BOTTOM UP 68 | def sortArray(A): 69 | if len(A) <= 1: 70 | return A 71 | mid = len(A) // 2 72 | left = A[:mid] 73 | left_sorted = [] 74 | while left: 75 | minimum = left[0] 76 | for x in left: 77 | if x < minimum: 78 | minimum = x 79 | left_sorted.append(minimum) 80 | left.remove(minimum) 81 | 82 | right = A[mid:] 83 | right_sorted = [] 84 | while right: 85 | minimum = right[0] 86 | for x in right: 87 | if x < minimum: 88 | minimum = x 89 | right_sorted.append(minimum) 90 | right.remove(minimum) 91 | 92 | merged = [] 93 | while left_sorted and right_sorted: 94 | if left_sorted[0] <= right_sorted[0]: 95 | merged.append(left_sorted.pop(0)) 96 | else: 97 | merged.append(right_sorted.pop(0)) 98 | merged.extend(right_sorted if right_sorted else left_sorted) 99 | return merged 100 | print(sortArray(A)) 101 | 102 | 103 | # 5. SORTING ITERATIVELY USING PYTHON'S BUILT IN SORTED FEATURE 104 | def merging(A): 105 | mid = len(A)//2 106 | left = sorted(A[:mid]) 107 | right = sorted(A[mid:]) 108 | C = [] 109 | while min(len(left), len(right)) > 0: 110 | if left[0] > right[0]: 111 | insert = right.pop(0) 112 | C.append(insert) 113 | elif left[0] <= right[0]: 114 | insert = left.pop(0) 115 | C.append(insert) 116 | if len(left) > 0: 117 | for i in left: 118 | C.append(i) 119 | if len(right) > 0: 120 | for i in right: 121 | C.append(i) 122 | return C 123 | print(merging(A)) 124 | 125 | 126 | 127 | _______________________________________________________ 128 | 129 | ADVANCED: MATRIX MULTIPLICATION 130 | 131 | 132 | # Naive Method: Multiplying two matrices using nested loops 133 | 134 | # 2X2 matrix "X" 135 | X = [[1, 2], 136 | [2, 3]] 137 | 138 | # 2X2 matrix "Y" 139 | Y = [[2, 3], 140 | [3, 4]] 141 | 142 | # 2X2 matrix of "0", which added to our answer, just gives us the answer 143 | result = [[0, 0], 144 | [0, 0]] 145 | 146 | # iterate through rows of X 147 | for i in range(len(X)): 148 | # iterate through columns of Y 149 | for j in range(len(Y[0])): 150 | # iterate through rows of Y 151 | for k in range(len(Y)): 152 | result[i][j] += X[i][k] * Y[k][j] 153 | 154 | # for end in result: 155 | # print(end) 156 | 157 | # Recursive code for Matrix Multiplication 158 | 159 | i = 0 160 | j = 0 161 | k = 0 162 | 163 | def multiplyMatrixRec(row1, col1, X, row2, col2, Y, result): 164 | 165 | if j < col2: 166 | if k < col1: 167 | result[i][j] += X[i][k] * Y[k][j] 168 | k += 1 169 | multiplyMatrixRec(row1, col1, X, row2, col2, Y, result) 170 | j += 1 171 | multiplyMatrixRec(row1, col1, X, row2, col2, Y, result) 172 | i += 1 173 | multiplyMatrixRec(row1, col1, X, row2, col2, Y, result) 174 | 175 | 176 | def multiplyMatrix(row1, col1, X, row2, col2, Y): 177 | for i in range(row1): 178 | for j in range(col2): 179 | print(result[i][j], end=" ") 180 | print() 181 | 182 | row1 = 2 183 | col1 = 2 184 | row2 = 2 185 | col2 = 2 186 | multiplyMatrix(row1, col1, X, row2, col2, Y) 187 | 188 | 189 | _______________________________________________________ 190 | 191 | MINDBREAKER: STRASSEN ALGORITHM 192 | 193 | # ITERATIVE PROGRAM 194 | 195 | import numpy as np 196 | 197 | x = np.array([[1, 2], [2, 3]]) 198 | y = np.array([[2, 3], [3, 4]]) 199 | 200 | 201 | def strassen_iter(x, y): 202 | # Base case when size of matrices is 1x1 203 | if len(x) == 1: 204 | return x * y 205 | 206 | # Splitting the matrices into quadrants. See graphic 207 | a, b, c, d = x[0, 0], x[0, 1], x[1, 0], x[1, 1] 208 | e, f, g, h = y[0, 0], y[0, 1], y[1, 0], y[1, 1] 209 | 210 | # Computing the 7 products - this is where the magic happens! 211 | p1 = a * (f - h) 212 | p2 = (a + b) * h 213 | p3 = (c + d) * e 214 | p4 = d * (g - e) 215 | p5 = (a + d) * (e + h) 216 | p6 = (b - d) * (g + h) 217 | p7 = (a - c) * (e + f) 218 | 219 | # Computing the values of the 4 quadrants of the final matrix c 220 | c1 = (p5 + p4 - p2 + p6) 221 | c2 = (p1 + p2) 222 | c3 = (p3 + p4) 223 | c4 = (p1 + p5 - p3 - p7) 224 | 225 | return np.array([[c1, c2], [c3, c4]]) 226 | 227 | #print(strassen_iter(x, y)) 228 | 229 | # ------------------------------------------- 230 | # RECURSIVE PROGRAM 231 | 232 | def split(matrix): 233 | row, col = matrix.shape 234 | row2, col2 = row // 2, col // 2 235 | return matrix[:row2, :col2], matrix[:row2, col2:], matrix[row2:, :col2], matrix[row2:, col2:] 236 | 237 | 238 | def strassen_recur(x, y): 239 | if len(x) == 1: 240 | return x * y 241 | 242 | # Splitting the matrices into quadrants. This will be done recursively until the base case is reached. 243 | a, b, c, d = split(x) 244 | e, f, g, h = split(y) 245 | 246 | # Computing the 7 products, recursively (p1, p2...p7) 247 | p1 = strassen_recur(a, f - h) 248 | p2 = strassen_recur(a + b, h) 249 | p3 = strassen_recur(c + d, e) 250 | p4 = strassen_recur(d, g - e) 251 | p5 = strassen_recur(a + d, e + h) 252 | p6 = strassen_recur(b - d, g + h) 253 | p7 = strassen_recur(a - c, e + f) 254 | 255 | # Computing the values of the 4 quadrants of the final matrix c 256 | c1 = (p5 + p4 - p2 + p6) 257 | c2 = (p1 + p2) 258 | c3 = (p3 + p4) 259 | c4 = (p1 + p5 - p3 - p7) 260 | 261 | # Combining the 4 quadrants into a single matrix by stacking horizontally and vertically. 262 | c = np.vstack((np.hstack((c1, c2)), np.hstack((c3, c4)))) 263 | 264 | return c 265 | 266 | print(strassen_recur(x, y)) 267 | -------------------------------------------------------------------------------- /4_greedy_algorithms: -------------------------------------------------------------------------------- 1 | The paradigm behind the greedy concept is that it builds up a solution piece by piece, 2 | always choosing the next piece that offers the most obvious and immediate benefit. 3 | By using several iterations, and by obtaining the best result, at a certain iteration 4 | the result should be computed. In other words, it follows the problem solving method of 5 | making the locally optimum choice at each stage with the hope of finding the global optimum. 6 | 7 | Basic- Mice In The Hole 8 | Advanced- Fractional Knapsack 9 | Mindbreaker- Egyptian Fractions (Fibonacci) 10 | 11 | ________________________________________________EXAMPLES__________________________________________________________ 12 | 13 | 14 | BASIC - MICE IN HOLES 15 | 16 | # Returns minimum time required to place mice in holes using the Greedy approach. We can put every mouse 17 | # to its nearest hole to minimize the time. This can be done by sorting the positions of mice and holes. 18 | 19 | def assignHole(mice, holes): 20 | 21 | # Base - num of mice and holes should be the same 22 | if len(mice) != len(holes): 23 | return "Number of mice and holes not the same" 24 | 25 | # Sort first 26 | mice.sort() 27 | holes.sort() 28 | 29 | # Finding max difference between ith mice and hole 30 | max_diff = 0 31 | 32 | for i in range(len(mice)): 33 | if max_diff < abs(mice[i] - holes[i]): 34 | max_diff = abs(mice[i] - holes[i]) 35 | 36 | return max_diff 37 | 38 | 39 | mice = [4, -4, 2] 40 | 41 | # Hole positions 42 | holes = [4, 0, 5] 43 | 44 | # The required answer is returned from the function 45 | min_time = assignHole(mice, holes) 46 | 47 | print("The last mouse gets into the hole in time:", min_time) 48 | 49 | 50 | ______________________________________________________________________ 51 | 52 | ADVANCED: FRACTIONAL KNAPSACK 53 | 54 | def fractional_knapsack(value, weight, capacity): 55 | 56 | items = list(range(len(value))) 57 | print(items) 58 | ratio = [v//w for v, w in zip(value, weight)] 59 | print(ratio) 60 | srt_ratios = sorted(ratio, reverse=True) 61 | print(srt_ratios) 62 | items.sort(key=lambda i: ratio[i], reverse=True) 63 | print(items) 64 | 65 | max_value = 0 66 | fractions = [0] * len(value) 67 | print(fractions) 68 | for i in items: 69 | if weight[i] <= capacity: 70 | fractions[i] = 1 71 | max_value += value[i] 72 | capacity -= weight[i] 73 | print(max_value) 74 | else: 75 | fractions[i] = capacity // weight[i] 76 | max_value += value[i] * capacity // weight[i] 77 | 78 | return max_value 79 | 80 | weight = [30, 50, 10, 70, 40] 81 | value = [150, 100, 90, 140, 120] 82 | capacity = 150 83 | print(fractional_knapsack(value, weight, capacity)) 84 | 85 | 86 | ____________________________________________________________________ 87 | 88 | 89 | MINDBREAKER: EGYPTIAN FRACTIONS 90 | 91 | # Python3 program to print an Egyptian fraction in Egyptian Form using Greedy Algorithm 92 | 93 | import math 94 | import fractions 95 | import functools 96 | 97 | def main(): 98 | f = fractions.Fraction(3, 4) 99 | e = to_egyptian_fractions(f) 100 | print(*e, sep=' + ') 101 | f = fractions.Fraction(6, 7) 102 | e = to_egyptian_fractions(f) 103 | print(*e, sep=' + ') 104 | f = fractions.Fraction(7654, 321) 105 | e = to_egyptian_fractions(f) 106 | print(*e, sep=' + ') 107 | 108 | 109 | def validate(function): 110 | @functools.wraps(function) 111 | def wrapper(fraction): 112 | total = fractions.Fraction(0) 113 | for egyptian in function(fraction): 114 | if 1 not in {egyptian.numerator, egyptian.denominator}: 115 | raise AssertionError('function has failed validation') 116 | yield egyptian 117 | total += egyptian 118 | if total != fraction: 119 | raise AssertionError('function has failed validation') 120 | return wrapper 121 | 122 | 123 | @validate 124 | def to_egyptian_fractions(fraction): 125 | quotient = math.floor(fraction.numerator / fraction.denominator) 126 | if quotient: 127 | egyptian = fractions.Fraction(quotient, 1) 128 | yield egyptian 129 | fraction -= egyptian 130 | while fraction: 131 | quotient = math.ceil(fraction.denominator / fraction.numerator) 132 | egyptian = fractions.Fraction(1, quotient) 133 | yield egyptian 134 | fraction -= egyptian 135 | 136 | 137 | # if __name__ == '__main__': 138 | # main() 139 | 140 | 141 | #-------------------------------- 142 | 143 | --VERY SIMPLISTIC PROGRAM-- 144 | 145 | def egyptian_frac(numerator, denominator): 146 | # Creating our list of denominators for our Eqyptian Fractions 147 | egypt_lst = [] 148 | while numerator != 0: 149 | x = math.ceil(denominator/numerator) 150 | egypt_lst.append(x) 151 | 152 | numerator = x * numerator - denominator 153 | denominator *= x 154 | str = "" 155 | for ones in egypt_lst: 156 | str += "1/{0} + ".format(ones) 157 | final_string = str[:-3] 158 | return final_string 159 | 160 | print(egyptian_frac(7, 12)) 161 | -------------------------------------------------------------------------------- /5_dynamic_programming: -------------------------------------------------------------------------------- 1 | In the world of dynamic programming, DP is used when the solution to a problem can be viewed as the result 2 | of a sequence of decisions with the intended outcome of reducing run time, and any implementation that 3 | can make a solution more efficient, is considered optimizing it. 4 | 5 | Basic- Ugly Numbers 6 | Advanced- Traveling Salesman Problem 7 | Mindbreaker- Palindromic Matrix Paths 8 | 9 | _____________________________________________________EXAMPLES_________________________________________________ 10 | 11 | 12 | BASIC- UGLY NUMBERS: 13 | 14 | # Recursive function for successive divisions 15 | 16 | def successive_div(x, y): 17 | while x % y == 0: 18 | x = x / y 19 | return x 20 | print(successive_div(6, 2)) 21 | 22 | # Function for checking if a number is ugly or not 23 | def ugly_check(num): 24 | num = successive_div(num, 2) 25 | num = successive_div(num, 3) 26 | num = successive_div(num, 5) 27 | if num == 1: 28 | return True 29 | else: 30 | return False 31 | print(ugly_check(6)) 32 | 33 | # Function for finding the nth ugly number 34 | def nth_ugly(n): 35 | i = 1 36 | # ugly number count 37 | counter = 1 38 | 39 | # Looping through all integers until ugly count becomes n 40 | while n > counter: 41 | i += 1 42 | if ugly_check(i): 43 | counter += 1 44 | return i 45 | 46 | no = nth_ugly(15) 47 | print("15th Ugly number is:", no) 48 | 49 | 50 | 51 | # DP method 52 | 53 | def nthUgly(n): 54 | dpUgly = [0] * n 55 | dpUgly[0] = 1 56 | 57 | u2 = u3 = u5 = 0 58 | 59 | multiple_2 = 2 60 | multiple_3 = 3 61 | multiple_5 = 5 62 | 63 | for i in range(1, n): 64 | dpUgly[i] = min(multiple_2, multiple_3, multiple_5) 65 | 66 | if dpUgly[i] == multiple_2: 67 | u2 += 1 68 | multiple_2 = dpUgly[u2] * 2 69 | 70 | if dpUgly[i] == multiple_3: 71 | u3 += 1 72 | multiple_3 = dpUgly[u3] * 3 73 | 74 | if dpUgly[i] == multiple_5: 75 | u5 += 1 76 | multiple_5 = dpUgly[u5] * 5 77 | 78 | return dpUgly[n - 1] 79 | 80 | n = 15 81 | #print("15th ugly number is:", nthUgly(n)) 82 | 83 | __________________________________________________________________ 84 | 85 | 86 | ADVANCED- TRAVELING SALESMAN PROBLEM 87 | 88 | from itertools import permutations 89 | V = 4 90 | 91 | def travel_salesman_problem(graph, s): 92 | # store all vertices 93 | vertex = [] 94 | for i in range(V): 95 | if i != s: 96 | vertex.append(i) 97 | #print(vertex) 98 | 99 | min_path = [] 100 | next_permutation = permutations(vertex) 101 | 102 | for i in next_permutation: 103 | current_pathweight = 0 104 | 105 | k = s 106 | for j in i: 107 | current_pathweight += graph[k][j] 108 | #print(j, i, current_pathweight, graph[k][j]) 109 | k = j 110 | current_pathweight += graph[k][s] 111 | min_path.append(current_pathweight) 112 | x = sorted(min_path) 113 | 114 | return x[0] 115 | 116 | if __name__ == "__main__": 117 | # matrix representation of graph 118 | graph = [[0, 10, 15, 20], 119 | [10, 0, 35, 25], 120 | [15, 35, 0, 30], 121 | [20, 25, 30, 0]] 122 | s = 0 123 | print(travel_salesman_problem(graph, s)) 124 | 125 | 126 | 127 | ______________________________________________________________________ 128 | 129 | MINDBREAKER- PALINDROMIC MATRIX PATHS: 130 | 131 | --------------UNIQUE PATHS 132 | def paths(m, n): 133 | row = [1] * n 134 | print(row) 135 | for i in range(m-1): 136 | newRow = [1] * n 137 | for j in range(n-2, -1, -1): 138 | newRow[j] = newRow[j+1] + row[j] 139 | row = newRow 140 | return row[0] 141 | 142 | print(paths(3, 3)) 143 | 144 | 145 | # THE PATHS 146 | # aaaa (0, 0) -> (0, 1) -> (0, 2) -> (1, 2) -> (2, 2) 147 | # aaba (0, 0) -> (0, 1) -> (1, 1) -> (2, 1) -> (2, 2) 148 | # aabb (0, 0) -> (0, 1) -> (1, 1) -> (2, 1) -> (2, 2) 149 | # abba (0, 0) -> (1, 0) -> (2, 0) -> (2, 1) -> (2, 2) 150 | 151 | # [(0, 1), (0, 2), (1, 1), (1, 2)] 152 | # 0, 0, 3, 2 153 | 154 | ------------------------------------------------------------------- 155 | 156 | import itertools 157 | a = [['a', 'a', 'a'], 158 | ['b', 'b', 'a'], 159 | ['a', 'b', 'a']] 160 | x = list(itertools.product(*a)) 161 | #print(x) 162 | 163 | p_1 = [] 164 | for i in x: 165 | y = "".join(i) 166 | p_1.append(y) 167 | # print(p_1) 168 | # print(set(p_1)) 169 | 170 | def isPalin_2(p_1): 171 | p_2 = [] 172 | for x in set(p_1): 173 | if x == x[::-1]: 174 | p_2.append(x) 175 | return p_2 176 | #print(isPalin_2(p_1)) 177 | 178 | ------------------------------------------------------------------- 179 | # palindromic paths from top left to bottom right in a grid. 180 | 181 | def is_palindrome(string): 182 | if string == string[::-1]: 183 | return True 184 | else: 185 | return False 186 | 187 | # i and j are row and column indexes of top left corner (these are 0, 0) -- m and n are bottom right corner (4,3) 188 | 189 | def palindromic_path(string, a, i, j, m, n): 190 | 191 | # See slides of path traversal - show as lists 192 | if j < m - 1 or i < n - 1: 193 | if i < n - 1: 194 | palindromic_path(string + a[i][j], a, i + 1, j, m, n) 195 | if j < m - 1: 196 | palindromic_path(string + a[i][j], a, i, j + 1, m, n) 197 | 198 | # If we reach bottom right corner (or end of the path), we go to is_palindrome function to check it. 199 | else: 200 | string = string + a[n - 1][m - 1] 201 | if is_palindrome(string): 202 | print(string) 203 | 204 | a = [['a', 'a', 'a'], 205 | ['b', 'b', 'a'], 206 | ['a', 'b', 'a']] 207 | 208 | str = "" 209 | #print(palindromic_path(str, a, 0, 0, 3, 3)) 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # intro_to_algorithms 2 | RealToughCandy.io Introduction to Algorithms course code examples. Enjoy! 3 | 4 | 5 | This repository contains all of the code samples used in RealToughCandy.io's Introduction to Algorithms course. 6 | 7 | Algorithms with example programs discussed: 8 | 9 | 1. Simple Recursive Algorithms: 10 | 11 | Examples: 12 | A - Basic 13 | Factorials 14 | B - Advanced 15 | Permutations 16 | C - Mind Breaker 17 | N-Queens Puzzle 18 | 19 | 20 | 2. Data Structures: 21 | 22 | Examples: 23 | A - Basic 24 | Array manipulation 25 | B - Advanced 26 | Linked lists 27 | C - Mind Breaker 28 | Hash tables 29 | 30 | 31 | 3. Divide & Conquer: 32 | 33 | Examples: 34 | A - Basic 35 | Merge sort 36 | B - Advanced 37 | Matrix multiplication 38 | C - Mind Breaker 39 | Straussen algorithm 40 | 41 | 42 | 43 | 4. Greedy Algorithms: 44 | 45 | Examples: 46 | A - Basic 47 | Mice in the hole/Try on your own- Kids with candies (LC #1431) 48 | B - Advanced 49 | Fractional Knapsack 50 | C - Mind Breaker 51 | Egyptian fractions - Fibonacci 52 | 53 | 54 | 5. Dynamic Programming: 55 | 56 | Examples: 57 | A - Basic 58 | Ugly numbers 59 | B - Advanced 60 | Traveling Salesman Problem 61 | C - Mind Breaker 62 | Palindromic matrix paths 63 | --------------------------------------------------------------------------------