├── .gitignore ├── .idea ├── .gitignore ├── algoexpert.iml ├── dictionaries │ └── josancamon19.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── 1.0. two-number-sum.py ├── 1.1. closest-bst-value.py ├── 1.2. bst-construction.py ├── 1.3. is_palindrome.py ├── 1.4. doubly- linked-list-construction.py ├── 1.5. fibonacci.py ├── 1.6. binary_search.py ├── 1.7. depth-first-search.py ├── 1.8. product-sum.py ├── 1.9. three-largest-sum.py ├── 2.0. bubble-sort.py ├── 2.1. bst-traversal.py ├── 2.2. breadth-first-search.py ├── 2.3. insertion-sort.py ├── 2.4. validate-bst.py ├── 2.5. selection-sort.py ├── 2.6. caesar-cipher-encryptor.py ├── 2.7. permutations.py ├── 2.8. smallest-difference.py ├── 2.9. three-number-sum.py ├── 3.0. merge-sort.py ├── 3.1. quicksort.py ├── 3.2. suffix-tree-construction.py ├── 3.3. reverse-linked-list.py ├── 3.4. shifted-binary-search.py ├── 3.5. balanced-brackets.py ├── 3.6. validate-bst.py ├── 3.7. powerset.py ├── 3.8. min-max-stack.py └── 3.9. longest-palindromic-substring.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /venv/ -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/algoexpert.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /.idea/dictionaries/josancamon19.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | algo 5 | encryptor 6 | logn 7 | nlogn 8 | powerset 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /1.0. two-number-sum.py: -------------------------------------------------------------------------------- 1 | def two_number_sum(array, target_sum): 2 | for n in array: 3 | residual = target_sum - n 4 | if residual in array and residual != n: 5 | return sorted([residual, n]) 6 | return [] 7 | 8 | 9 | if __name__ == '__main__': 10 | print(two_number_sum([3, 5, -4, 8, 11, 1, -1, 6], 10)) 11 | -------------------------------------------------------------------------------- /1.1. closest-bst-value.py: -------------------------------------------------------------------------------- 1 | def findClosestValueInBst(tree, target): 2 | closest_value = float('inf') 3 | node = tree 4 | while node: 5 | if abs(target - closest_value) > abs(target - node.value): 6 | closest_value = node.value 7 | 8 | if node.value > target: 9 | if node.left is None: 10 | return closest_value 11 | node = node.left 12 | 13 | elif node.value < target: 14 | if node.right is None: 15 | return closest_value 16 | node = node.right 17 | 18 | else: 19 | return node.value 20 | -------------------------------------------------------------------------------- /1.2. bst-construction.py: -------------------------------------------------------------------------------- 1 | def traverse_list(node): 2 | visit_order = list() 3 | if node: 4 | visit_order.append(node.value) 5 | visit_order += traverse_list(node.left) 6 | visit_order += traverse_list(node.right) 7 | return visit_order 8 | 9 | 10 | def traverse(node): 11 | visit_order = list() 12 | if node: 13 | visit_order.append(node) 14 | visit_order += traverse(node.left) 15 | visit_order += traverse(node.right) 16 | return visit_order 17 | 18 | 19 | def get_min_node_value(node): 20 | while node.left: 21 | node = node.left 22 | return node.value 23 | 24 | 25 | class BST: 26 | def __init__(self, value): 27 | self.value = value 28 | self.left = None 29 | self.right = None 30 | 31 | def compare(self, target): 32 | if self.value > target: 33 | return -1 34 | elif self.value < target: 35 | return 1 36 | else: 37 | return 0 38 | 39 | def insert(self, value): 40 | 41 | node = self 42 | while True: 43 | comparision = node.compare(value) 44 | if comparision == -1: 45 | if node.left: 46 | node = node.left 47 | else: 48 | node.left = BST(value) 49 | break 50 | else: # comparision == 1 or equals 51 | if node.right: 52 | node = node.right 53 | else: 54 | node.right = BST(value) 55 | break 56 | 57 | return self 58 | 59 | def contains(self, value): 60 | node = self 61 | while node: 62 | comparision = node.compare(value) 63 | if comparision == -1: 64 | node = node.left 65 | elif comparision == 1: 66 | node = node.right 67 | else: 68 | return True 69 | 70 | return False 71 | 72 | def remove(self, value, parent_node=None): 73 | node = self 74 | while True: 75 | comparision = node.compare(value) 76 | if comparision == -1: 77 | if node.left: 78 | parent_node = node 79 | node = node.left 80 | else: 81 | print('Value not found') 82 | break 83 | elif comparision == 1: 84 | if node.right: 85 | parent_node = node 86 | node = node.right 87 | else: 88 | print('Value not found') 89 | break 90 | else: 91 | if node.left and node.right: # node with left and child 92 | node.value = get_min_node_value(node.right) 93 | node.right.remove(node.value, node) 94 | elif parent_node is None: # parent node 95 | if node.left: 96 | node.value = node.left.value 97 | node.right = node.left.right 98 | node.left = node.left.left 99 | elif node.right: 100 | node.value = node.right.value 101 | node.left = node.right.left 102 | node.right = node.right.right 103 | else: # parent node with no children 104 | node.value = None 105 | 106 | elif parent_node.left == node: # found in the left node with right None 107 | parent_node.left = node.left if node.left else node.right 108 | elif parent_node.right == node: # found in the right node with left None 109 | parent_node.right = node.left if node.left else node.right 110 | break 111 | 112 | return self 113 | 114 | 115 | if __name__ == '__main__': 116 | # tree = BST(10) 117 | # tree.insert(5) 118 | # tree.insert(8) 119 | # tree.insert(3) 120 | # tree.insert(4) 121 | # tree.insert(1) 122 | # tree.insert(2) 123 | # tree.insert(0) 124 | # print(traverse_list(tree)) 125 | # 126 | # tree.remove(5) 127 | # print(traverse_list(tree)) 128 | test = BST(10).insert(5).insert(7).insert(2).remove(10) 129 | print(traverse_list(test)) 130 | -------------------------------------------------------------------------------- /1.3. is_palindrome.py: -------------------------------------------------------------------------------- 1 | def is_palindrome(string): 2 | # Write your code here. 3 | for i in range(len(string) // 2): 4 | if string[i] != string[len(string) - 1 - i]: 5 | return False 6 | return True 7 | -------------------------------------------------------------------------------- /1.4. doubly- linked-list-construction.py: -------------------------------------------------------------------------------- 1 | # Feel free to add new properties and methods to the class. 2 | # noinspection PyPep8Naming 3 | class Node: 4 | def __init__(self, value): 5 | self.value = value 6 | self.next = None 7 | self.prev = None 8 | 9 | def __repr__(self): 10 | return str(self.value) 11 | 12 | def get_prev_value(self): 13 | if self.prev is None: 14 | return 'None' 15 | return str(self.prev.value) 16 | 17 | def get_next_value(self): 18 | if self.next is None: 19 | return 'None' 20 | return str(self.next.value) 21 | 22 | def __str__(self): 23 | return 'Node(prev:' + self.get_prev_value() + ', this:' + str( 24 | self.value) + ', next:' + self.get_next_value() + ')' 25 | 26 | def __eq__(self, other): 27 | if other: 28 | return self.value == other.value 29 | return self is None 30 | 31 | 32 | # noinspection PyPep8Naming 33 | class DoublyLinkedList: 34 | def __init__(self): 35 | self.head = None 36 | self.tail = None 37 | 38 | def setHead(self, node): 39 | # Write your code here. 40 | if self.head is None: 41 | self.head = node 42 | self.tail = node 43 | return 44 | 45 | self.insertBefore(self.head, node) 46 | 47 | def setTail(self, node): 48 | # Write your code here. 49 | 50 | if self.tail is None: 51 | self.setHead(node) 52 | return 53 | 54 | self.insertAfter(self.tail, node) 55 | 56 | def append(self, node): 57 | if self.head is None: 58 | self.setHead(node) 59 | return 60 | 61 | current = self.head 62 | while current.next is not None: 63 | current = current.next 64 | 65 | node.prev = current 66 | current.next = node 67 | 68 | def insertBefore(self, node, nodeToInsert): 69 | # Write your code here. 70 | if nodeToInsert == self.head and nodeToInsert == self.tail: 71 | return 72 | 73 | nodeToInsert.prev = node.prev 74 | nodeToInsert.next = node 75 | 76 | if node.prev is None: 77 | self.head = nodeToInsert 78 | else: 79 | node.prev.next = nodeToInsert 80 | node.prev = nodeToInsert 81 | 82 | def insertAfter(self, node, nodeToInsert): 83 | # Write your code here. 84 | if nodeToInsert == self.head and nodeToInsert == self.tail: 85 | return 86 | 87 | nodeToInsert.prev = node 88 | nodeToInsert.next = node.next 89 | 90 | if node.next is None: 91 | self.tail = nodeToInsert 92 | else: 93 | node.next.prev = nodeToInsert 94 | 95 | node.next = nodeToInsert 96 | 97 | def insertAtPosition(self, position, nodeToInsert): 98 | # Write your code here. 99 | if position == 1: 100 | self.setHead(nodeToInsert) 101 | return 102 | current = self.head 103 | index = 1 104 | while current and position != index: 105 | index += 1 106 | current = current.next 107 | 108 | if current is None: 109 | self.setTail(nodeToInsert) 110 | else: 111 | self.insertBefore(current, nodeToInsert) 112 | 113 | def removeNodesWithValue(self, value): 114 | # Write your code here. 115 | current = self.head 116 | while current: 117 | nxt = current.next 118 | if current.value == value: 119 | self.remove(current) 120 | 121 | current = nxt 122 | 123 | def remove(self, node): 124 | # Write your code here. 125 | 126 | if node == self.head: 127 | self.head = self.head.next 128 | 129 | if node == self.tail: 130 | self.tail = self.tail.prev 131 | 132 | self.removeNode(node) 133 | 134 | def removeNode(self, node): 135 | if node.next: 136 | node.next.prev = node.prev 137 | if node.prev: 138 | node.prev.next = node.next 139 | 140 | del node 141 | 142 | def containsNodeWithValue(self, value): 143 | # Write your code here. 144 | 145 | current = self.head 146 | while current: 147 | if current.value == value: 148 | return True 149 | current = current.next 150 | return False 151 | 152 | def __repr__(self): 153 | 154 | if self.head is None: 155 | return '------' 156 | 157 | statement = '' 158 | current = self.head 159 | while current: 160 | statement += str(current) 161 | current = current.next 162 | if current: 163 | statement += ' ---> ' 164 | 165 | return statement + '' 166 | 167 | # Initial Wrong solution 168 | # class DoublyLinkedList: 169 | # def __init__(self): 170 | # self.head = None 171 | # self.tail = None 172 | # 173 | # def setHead(self, node): 174 | # # Write your code here. 175 | # 176 | # if self.head is not None: 177 | # self.head.prev = node 178 | # 179 | # node.next = self.head 180 | # self.head = node 181 | # 182 | # def setTail(self, node): 183 | # # Write your code here. 184 | # 185 | # if self.head is None: 186 | # self.head = node 187 | # 188 | # if self.tail is not None: 189 | # self.tail.next = node 190 | # 191 | # node.prev = self.tail 192 | # self.tail = node 193 | # return self.head 194 | # 195 | # def append(self, node): 196 | # if self.head is None: 197 | # self.setHead(node) 198 | # return 199 | # 200 | # current = self.head 201 | # while current.next is not None: 202 | # current = current.next 203 | # 204 | # node.prev = current 205 | # current.next = node 206 | # 207 | # def insertBefore(self, node, nodeToInsert): 208 | # # Write your code here. 209 | # if node == self.head: 210 | # self.setHead(nodeToInsert) 211 | # return 212 | # 213 | # current = self.head 214 | # while current.next: 215 | # if current.next == node: 216 | # nodeToInsert.prev = current 217 | # nodeToInsert.next = current.next 218 | # 219 | # current.next.prev = nodeToInsert 220 | # current.next = nodeToInsert 221 | # break 222 | # current = current.next 223 | # 224 | # def insertAfter(self, node, nodeToInsert): 225 | # # Write your code here. 226 | # current = self.head 227 | # while current.next: 228 | # if current.next == node: 229 | # nodeToInsert.prev = current.next 230 | # nodeToInsert.next = current.next.next 231 | # 232 | # if current.next.next: 233 | # current.next.next.prev = nodeToInsert 234 | # current.next.next = nodeToInsert 235 | # break 236 | # current = current.next 237 | # 238 | # def insertAtPosition(self, position, nodeToInsert): 239 | # # Write your code here. 240 | # if position == 1: 241 | # self.setHead(nodeToInsert) 242 | # return 243 | # current = self.head 244 | # index = 1 245 | # while current: 246 | # if position == index + 1: 247 | # nodeToInsert.prev = current 248 | # nodeToInsert.next = current.next 249 | # if current.next: 250 | # current.next.prev = nodeToInsert 251 | # current.next = nodeToInsert 252 | # break 253 | # index += 1 254 | # current = current.next 255 | # 256 | # def removeNodesWithValue(self, value): 257 | # # Write your code here. 258 | # current = self.head 259 | # while current: 260 | # if current.value == value: 261 | # if current.prev: 262 | # current.prev.next = current.next 263 | # if current.next: 264 | # current.next.prev = current.prev 265 | # 266 | # current = current.next 267 | # 268 | # def remove(self, node): 269 | # # Write your code here. 270 | # current = self.head 271 | # while current: 272 | # if current == node: 273 | # if current.prev: 274 | # current.prev.next = current.next 275 | # if current.next: 276 | # current.next.prev = current.prev 277 | # break 278 | # 279 | # current = current.next 280 | # 281 | # def containsNodeWithValue(self, value): 282 | # # Write your code here. 283 | # if self.head is None: 284 | # return False 285 | # 286 | # current = self.head 287 | # while current: 288 | # if current.value == value: 289 | # return True 290 | # current = current.next 291 | # return False 292 | # 293 | # def __repr__(self): 294 | # if self.head is None: 295 | # return '------' 296 | # 297 | # statement = '' 298 | # current = self.head 299 | # while current: 300 | # statement += str(current) 301 | # current = current.next 302 | # if current: 303 | # statement += ' ---> ' 304 | # 305 | # return statement + '' 306 | # 307 | # 308 | # if __name__ == '__main__': 309 | # doubly = DoublyLinkedList() 310 | # 311 | # doubly.append(Node(1)) 312 | # doubly.append(Node(2)) 313 | # doubly.append(Node(4)) 314 | # # print(doubly) 315 | # 316 | # doubly.insertBefore(Node(4), Node(3)) 317 | # # print(doubly) 318 | # 319 | # doubly.insertAfter(Node(4), Node(5)) 320 | # # print(doubly) 321 | # 322 | # doubly.remove(Node(3)) 323 | # # print(doubly) 324 | # 325 | # doubly.append(Node(2)) 326 | # doubly.append(Node(2)) 327 | # # print(doubly) 328 | # 329 | # doubly.removeNodesWithValue(2) 330 | # print(doubly) 331 | # 332 | # doubly.insertAtPosition(2, Node(2)) 333 | # print(doubly) 334 | # 335 | # doubly.insertAtPosition(3, Node(3)) 336 | # print(doubly) 337 | # 338 | # doubly.insertAtPosition(6, Node(6)) 339 | # print(doubly) 340 | # 341 | # doubly.insertAtPosition(2, Node(1.5)) 342 | # print(doubly) 343 | -------------------------------------------------------------------------------- /1.5. fibonacci.py: -------------------------------------------------------------------------------- 1 | def get_fib(n): 2 | # Write your code here. 3 | if n == 1: 4 | return 0 5 | if n == 2: 6 | return 1 7 | 8 | return get_fib(n - 2) + get_fib(n - 1) 9 | 10 | 11 | if __name__ == '__main__': 12 | print(get_fib(6)) 13 | -------------------------------------------------------------------------------- /1.6. binary_search.py: -------------------------------------------------------------------------------- 1 | def binarySearch(array, target): 2 | # Write your code here. 3 | first = 0 4 | last = len(array) - 1 5 | while True: 6 | 7 | mid_idx = (last + first) // 2 8 | mid_element = array[mid_idx] 9 | 10 | if mid_element == target: 11 | return mid_idx 12 | elif first == last or first > last: 13 | return -1 14 | elif mid_element > target: 15 | last = mid_idx 16 | else: # mid_element < target 17 | first = mid_idx + 1 18 | 19 | 20 | if __name__ == '__main__': 21 | a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 22 | print(binarySearch(a, 5)) 23 | -------------------------------------------------------------------------------- /1.7. depth-first-search.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, name): 3 | self.children = [] 4 | self.name = name 5 | 6 | def __repr__(self): 7 | return str(self.name) 8 | 9 | def addChild(self, name): 10 | self.children.append(Node(name)) 11 | return self 12 | 13 | def depthFirstSearch(self, array): 14 | array.append(self.name) 15 | for n in self.children: 16 | n.depthFirstSearch(array) 17 | return array 18 | 19 | 20 | if __name__ == '__main__': 21 | root = Node('A') 22 | 23 | root.addChild('B') 24 | root.addChild('C') 25 | root.addChild('D') 26 | 27 | # append to B node E and F 28 | root.children[0].addChild('E') 29 | root.children[0].addChild('F') 30 | 31 | # append to D node G and H 32 | root.children[2].addChild('G') 33 | root.children[2].addChild('H') 34 | 35 | # append to F node I and J 36 | root.children[0].children[1].addChild('I') 37 | root.children[0].children[1].addChild('J') 38 | 39 | # append K to G node 40 | root.children[2].children[0].addChild('K') 41 | 42 | print(root.depthFirstSearch([])) 43 | -------------------------------------------------------------------------------- /1.8. product-sum.py: -------------------------------------------------------------------------------- 1 | def productSum(array): 2 | # Write your code here. 3 | return product_sum_with_depth(array) 4 | 5 | 6 | def product_sum_with_depth(array, depth=1): 7 | to_add = 0 8 | for item in array: 9 | if type(item) is list: 10 | to_add += (depth + 1) * product_sum_with_depth(item, depth + 1) 11 | else: 12 | to_add += item 13 | return to_add 14 | 15 | 16 | if __name__ == '__main__': 17 | print(productSum([5, 2, [7, -1], 3, [6, [-13, 8], 4]])) 18 | # print(productSum([6, [-13, 8], 4])) 19 | -------------------------------------------------------------------------------- /1.9. three-largest-sum.py: -------------------------------------------------------------------------------- 1 | # def findThreeLargestNumbers(array): 2 | # # Write your code here. 3 | # return sorted(array)[-3:] # sorted -> O(logn) 4 | # 5 | # find_three_largest = lambda array: sorted(array)[-3:] 6 | 7 | 8 | # def findThreeLargestNumbers(array): 9 | # # Write your code here. 10 | # result = array[:3] 11 | # for n in array[3:]: 12 | # min_result = min(result) 13 | # if n > min_result: 14 | # result[result.index(min_result)] = n 15 | # 16 | # return sorted(result) 17 | 18 | 19 | # This solution works for n largest len of result 20 | def findThreeLargestNumbers(array): 21 | # Write your code here. 22 | result = [None for _ in range(3)] 23 | for number in array: 24 | for i in range(len(result)): 25 | idx = len(result) - 1 - i 26 | print(number, result) 27 | if result[idx] is None: 28 | result[idx] = number 29 | break 30 | elif result[idx] < number: 31 | this = result[idx] 32 | if idx > 0: 33 | # TODO here go idx --- 0 and swapping nodes throwing 0 34 | result[idx - 1] = result[idx] 35 | result[idx] = number 36 | break 37 | 38 | return result 39 | 40 | 41 | if __name__ == '__main__': 42 | print(findThreeLargestNumbers([141, 1, 17, -7, -17, -27, 18, 541, 8, 7, 7])) 43 | # print(findThreeLargestNumbers([1, 2, 3, 4])) 44 | -------------------------------------------------------------------------------- /2.0. bubble-sort.py: -------------------------------------------------------------------------------- 1 | def bubbleSort(array): 2 | # Write your code here. 3 | for i in range(len(array) - 1): 4 | for j in range(len(array) - 1): 5 | if array[j] > array[j + 1]: 6 | array[j], array[j + 1] = array[j + 1], array[j] 7 | return array 8 | 9 | 10 | if __name__ == '__main__': 11 | print(bubbleSort([4, 3, 2, 1])) 12 | # print(bubbleSort([8, 5, 2, 9, 5, 6, 3])) 13 | -------------------------------------------------------------------------------- /2.1. bst-traversal.py: -------------------------------------------------------------------------------- 1 | def inOrderTraverse(tree, array): 2 | # Write your code here. 3 | if tree: 4 | inOrderTraverse(tree.left, array) 5 | array.append(tree.value) 6 | inOrderTraverse(tree.right, array) 7 | return array 8 | 9 | 10 | def preOrderTraverse(tree, array): 11 | # Write your code here. 12 | if tree: 13 | array.append(tree.value) 14 | preOrderTraverse(tree.left, array) 15 | preOrderTraverse(tree.right, array) 16 | return array 17 | 18 | 19 | def postOrderTraverse(tree, array): 20 | # Write your code here. 21 | if tree: 22 | postOrderTraverse(tree.left, array) 23 | postOrderTraverse(tree.right, array) 24 | array.append(tree.value) 25 | return array 26 | -------------------------------------------------------------------------------- /2.2. breadth-first-search.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, name): 3 | self.children = [] 4 | self.name = name 5 | 6 | def addChild(self, name): 7 | self.children.append(Node(name)) 8 | return self 9 | 10 | def breadthFirstSearch(self, array): 11 | queue = [self] 12 | while len(queue) > 0: 13 | node = queue.pop(-1) 14 | array.append(node.name) 15 | for child in node.children: 16 | queue.insert(0, child) 17 | 18 | return array 19 | 20 | 21 | if __name__ == '__main__': 22 | test1 = Node('A').addChild('B').addChild('C').addChild('D') 23 | test1.children[0].addChild('E').addChild('F') 24 | test1.children[2].addChild('G').addChild('H') 25 | test1.children[0].children[1].addChild('I').addChild('J') 26 | test1.children[2].children[0].addChild('K') 27 | 28 | print(test1.breadthFirstSearch([])) 29 | -------------------------------------------------------------------------------- /2.3. insertion-sort.py: -------------------------------------------------------------------------------- 1 | # def insertionSort(array): 2 | # for i in range(0, len(array) - 1): 3 | # this_idx = i 4 | # next_idx = i + 1 5 | # 6 | # while this_idx >= 0 and array[next_idx] < array[this_idx]: # is next less than this 7 | # array[this_idx], array[next_idx] = array[next_idx], array[this_idx] 8 | # next_idx -= 1 9 | # this_idx -= 1 10 | # return array 11 | 12 | def insertionSort(array): 13 | for i in range(0, len(array) - 1): 14 | while i >= 0 and array[i + 1] < array[i]: # is next less than this 15 | array[i], array[i + 1] = array[i + 1], array[i] 16 | i -= 1 17 | return array 18 | 19 | 20 | if __name__ == '__main__': 21 | print(insertionSort([3, 2, 1, 0])) 22 | -------------------------------------------------------------------------------- /2.4. validate-bst.py: -------------------------------------------------------------------------------- 1 | def compare(node_value, target): 2 | if node_value > target: 3 | return -1 4 | elif node_value < target: 5 | return 1 6 | else: 7 | return 0 8 | 9 | 10 | def validateBst(node): 11 | # Write your code here. 12 | if (node.left is None or node.value > node.left.value) and (node.right is None or node.value <= node.right.value): 13 | return validateBst() -------------------------------------------------------------------------------- /2.5. selection-sort.py: -------------------------------------------------------------------------------- 1 | def get_min(array): 2 | # print('Array to get min:', array) 3 | min_element = None 4 | for idx, value in enumerate(array): 5 | if min_element is None or value < min_element[1]: 6 | min_element = idx, value 7 | 8 | # print('Getting min element', min_element) 9 | return min_element[0] 10 | 11 | 12 | def selectionSort(array): 13 | # Write your code here. 14 | for i in range(len(array)): 15 | min_idx = get_min(array[i:]) + i 16 | # print(array[i], array[min_idx], array) 17 | array[i], array[min_idx] = array[min_idx], array[i] 18 | 19 | return array 20 | 21 | 22 | if __name__ == '__main__': 23 | print(selectionSort([3, 2, 1, 0])) 24 | -------------------------------------------------------------------------------- /2.6. caesar-cipher-encryptor.py: -------------------------------------------------------------------------------- 1 | def encode_char(char, key): 2 | # [ 97 - 122 ] --> [ a - z ] 3 | return str(chr(97 + (((ord(char) + key) - 97) % 26))) 4 | 5 | 6 | def caesarCipherEncryptor(string, key): 7 | # Write your code here. 8 | return ''.join([encode_char(char, key) for char in string]) 9 | 10 | 11 | if __name__ == '__main__': 12 | print(caesarCipherEncryptor('abc', 52)) 13 | -------------------------------------------------------------------------------- /2.7. permutations.py: -------------------------------------------------------------------------------- 1 | 2 | def getPermutations(array): 3 | if len(array) == 0: 4 | return [] 5 | elif len(array) == 1: 6 | return [array] 7 | permutations = [] 8 | 9 | for i in range(len(array)): 10 | array.insert(0, array.pop(i)) 11 | for perm in getPermutations(array[1:]): 12 | perm.insert(0, array[0]) 13 | permutations.append(perm) 14 | 15 | return permutations 16 | 17 | 18 | if __name__ == '__main__': 19 | print(getPermutations([1, 2, 3, 4])) 20 | -------------------------------------------------------------------------------- /2.8. smallest-difference.py: -------------------------------------------------------------------------------- 1 | # def smallestDifference(arrayOne, arrayTwo): # O(n2) 2 | # # Write your code here. 3 | # smallest = [float('inf'), float('-inf')] 4 | # for one in arrayOne: 5 | # for two in arrayTwo: 6 | # if abs(smallest[0] - smallest[1]) > abs(one - two): 7 | # smallest = [one, two] 8 | # return smallest 9 | 10 | def smallestDifference(arrayOne, arrayTwo): 11 | # Write your code here. 12 | arrayOne.sort() 13 | arrayTwo.sort() 14 | 15 | smallest = [float('inf'), float('-inf')] 16 | first_idx = 0 17 | second_idx = 0 18 | while True: 19 | first = arrayOne[first_idx] 20 | second = arrayTwo[second_idx] 21 | expected = abs(first - second) 22 | 23 | if abs(smallest[0] - smallest[1]) > expected: 24 | smallest = [first, second] 25 | 26 | if first == second: 27 | break 28 | elif first < second: 29 | if first_idx + 1 < len(arrayOne): 30 | first_idx += 1 31 | else: 32 | break 33 | else: # first > second 34 | if second_idx + 1 < len(arrayTwo): 35 | second_idx += 1 36 | else: 37 | break 38 | 39 | return smallest 40 | 41 | 42 | if __name__ == '__main__': 43 | smallestDifference([1, 2, 3, 4], [3, 2, 1]) 44 | -------------------------------------------------------------------------------- /2.9. three-number-sum.py: -------------------------------------------------------------------------------- 1 | def threeNumberSum(array, targetSum): 2 | array.sort() # nlogn 3 | results = [] 4 | for current in range(len(array)): 5 | left = current + 1 6 | right = len(array) - 1 7 | while left < right: 8 | result = array[current] + array[left] + array[right] 9 | if result == targetSum: 10 | results.append([array[current], array[left], array[right]]) 11 | left += 1 12 | right -= 1 13 | elif result < targetSum: 14 | left += 1 15 | else: 16 | right -= 1 17 | return results 18 | 19 | 20 | if __name__ == '__main__': 21 | print(threeNumberSum([12, 3, 1, 2, -6, 5, -8, 6], 0)) 22 | print(threeNumberSum([1, 2, 3, 4, 5, 6, 7, 8, 9, 15], 33)) 23 | print(threeNumberSum([1], 10)) 24 | print(threeNumberSum([1, 2, 3, 7, 0], 10)) 25 | -------------------------------------------------------------------------------- /3.0. merge-sort.py: -------------------------------------------------------------------------------- 1 | def mergeSort(array): 2 | for i, item in enumerate(array): 3 | array[i] = [item] 4 | 5 | print(array) 6 | 7 | 8 | def divide_array(array): 9 | pass 10 | 11 | 12 | def sort_pair_arrays(array1, array2): 13 | if array1 is None or len(array1) == 0: 14 | return array2 15 | if array2 is None or len(array2) == 0: 16 | return array1 17 | 18 | new_array = [] 19 | first_idx, second_idx = 0, 0 20 | while first_idx < len(array1) or second_idx < len(array2): 21 | 22 | if first_idx >= len(array1): 23 | new_array += array2[second_idx:] 24 | break 25 | 26 | if second_idx >= len(array2): 27 | new_array += array1[first_idx:] 28 | break 29 | 30 | first = array1[first_idx] 31 | second = array2[second_idx] 32 | 33 | if first <= second: 34 | new_array.append(first) 35 | first_idx += 1 36 | elif second < first: 37 | new_array.append(second) 38 | second_idx += 1 39 | 40 | return new_array 41 | 42 | 43 | if __name__ == '__main__': 44 | print(mergeSort([8, 2, 5, 9, 5, 6, 3])) 45 | # print(sort_pair_arrays([1], [2])) 46 | -------------------------------------------------------------------------------- /3.1. quicksort.py: -------------------------------------------------------------------------------- 1 | # def quickSort(arr): 2 | # if len(arr) <= 1: 3 | # return arr 4 | # 5 | # def sort(array, pivot_idx, right_idx): 6 | # left_idx = pivot_idx + 1 7 | # while right_idx >= left_idx: 8 | # pivot = array[pivot_idx] 9 | # left = array[left_idx] 10 | # right = array[right_idx] 11 | # 12 | # if left > pivot > right: 13 | # swap(array, left_idx, right_idx) 14 | # if left <= pivot: 15 | # left_idx += 1 16 | # if right >= pivot: 17 | # right_idx -= 1 18 | # 19 | # # print(left_idx, right_idx, array) 20 | # swap(array, pivot_idx, right_idx) 21 | # # print(array) 22 | # # now right idx is ok lets do the same for array[:right_idx] and array[right_idx + 1:] 23 | # return quickSort(array[:right_idx]) + [array[right_idx]] + quickSort(array[right_idx + 1:]) 24 | # 25 | # # print('------') 26 | # return sort(arr, 0, len(arr) - 1) 27 | 28 | def quickSort(arr): 29 | sort_last_as_pivot(arr, 0, len(arr) - 1) 30 | return arr 31 | 32 | 33 | def sort_first_as_pivot(array, start_idx, end_idx): # sort using the first index as pivot 34 | if start_idx >= end_idx: 35 | return 36 | 37 | pivot_idx = start_idx 38 | left_idx = pivot_idx + 1 39 | right_idx = end_idx 40 | 41 | while right_idx >= left_idx: 42 | pivot = array[pivot_idx] 43 | left = array[left_idx] 44 | right = array[right_idx] 45 | 46 | if left > pivot > right: 47 | swap(array, left_idx, right_idx) 48 | if left <= pivot: 49 | left_idx += 1 50 | if right >= pivot: 51 | right_idx -= 1 52 | 53 | swap(array, pivot_idx, right_idx) 54 | 55 | # print(pivot_idx, start_idx, left_idx, right_idx, end_idx) 56 | left_smaller = right_idx - 1 - start_idx < end_idx - (right_idx + 1) 57 | if left_smaller: 58 | sort_first_as_pivot(array, start_idx, right_idx - 1) 59 | sort_first_as_pivot(array, right_idx + 1, end_idx) 60 | else: 61 | sort_first_as_pivot(array, right_idx + 1, end_idx) 62 | sort_first_as_pivot(array, start_idx, right_idx - 1) 63 | 64 | 65 | def sort_last_as_pivot(array, start_idx, end_idx): # sort using the last index as pivot 66 | if start_idx >= end_idx: 67 | return 68 | 69 | pivot_idx = end_idx 70 | left_idx = start_idx 71 | right_idx = pivot_idx - 1 72 | 73 | while right_idx >= left_idx: 74 | pivot = array[pivot_idx] 75 | left = array[left_idx] 76 | right = array[right_idx] 77 | 78 | if left > pivot > right: 79 | swap(array, left_idx, right_idx) 80 | if left <= pivot: 81 | left_idx += 1 82 | if right >= pivot: 83 | right_idx -= 1 84 | 85 | swap(array, pivot_idx, left_idx) 86 | 87 | # print(array) 88 | 89 | left_smaller = right_idx - 1 - start_idx < end_idx - (right_idx + 1) 90 | if left_smaller: 91 | sort_last_as_pivot(array, start_idx, left_idx - 1) 92 | sort_last_as_pivot(array, left_idx + 1, end_idx) 93 | else: 94 | sort_last_as_pivot(array, left_idx + 1, end_idx) 95 | sort_last_as_pivot(array, start_idx, left_idx - 1) 96 | 97 | 98 | def swap(array, i, j): 99 | array[i], array[j] = array[j], array[i] 100 | 101 | 102 | if __name__ == '__main__': 103 | # print(quickSort([-2, 3, 2, 4, - 1, 1, 0, -1, 9, -10])) 104 | print(quickSort([-1, 8, 5, 2, 9, 5, 6, 3])) 105 | -------------------------------------------------------------------------------- /3.2. suffix-tree-construction.py: -------------------------------------------------------------------------------- 1 | # Do not edit the class below except for the 2 | # populateSuffixTrieFrom and contains methods. 3 | # Feel free to add new properties and methods 4 | # to the class. 5 | 6 | # AlgoExpert solution is too inefficient 7 | # I think this one is better 8 | class SuffixTrie: 9 | def __init__(self, string): 10 | self.root = {} 11 | self.endSymbol = "*" 12 | self.populateSuffixTrieFrom(string) 13 | 14 | def populateSuffixTrieFrom(self, string): 15 | # Write your code here. 16 | node = self.root 17 | for char in string: 18 | if char not in node: 19 | node[char] = {} 20 | node = node[char] 21 | node["*"] = True 22 | 23 | def contains(self, string): 24 | # Write your code here. 25 | node = self.root 26 | for char in string: 27 | if char not in node: 28 | return False 29 | node = node[char] 30 | 31 | # print(node) 32 | return '*' in node 33 | 34 | def get_prefix_node(self, prefix): 35 | # Write your code here. 36 | node = self.root 37 | for char in prefix: 38 | if char not in node: 39 | return None 40 | node = node[char] 41 | 42 | return node 43 | 44 | def get_suffixes(self, prefix): 45 | node = self.get_prefix_node(prefix) 46 | return suffixes_from_node(node, '') 47 | 48 | 49 | def suffixes_from_node(node, previous): 50 | results = [] 51 | for key, child in node.items(): 52 | if type(child) == dict: 53 | if '*' in child: 54 | results.append(previous + key) 55 | results += suffixes_from_node(child, previous + key) 56 | return results 57 | 58 | 59 | if __name__ == '__main__': 60 | test1 = SuffixTrie('test') 61 | test1.populateSuffixTrieFrom('test2') 62 | test1.populateSuffixTrieFrom('testsito') 63 | print(test1.get_suffixes('te')) 64 | -------------------------------------------------------------------------------- /3.3. reverse-linked-list.py: -------------------------------------------------------------------------------- 1 | def reverseLinkedList(head): 2 | # Write your code here. 3 | previous = None 4 | current = head 5 | while current: 6 | nxt = current.next 7 | current.next = previous 8 | previous = current 9 | if nxt is None: 10 | break 11 | current = nxt 12 | return current 13 | -------------------------------------------------------------------------------- /3.4. shifted-binary-search.py: -------------------------------------------------------------------------------- 1 | def shiftedBinarySearch(array, target): 2 | # Write your code here. 3 | pivot = find_pivot(array, 0, len(array) - 1) 4 | if target == array[pivot]: 5 | return pivot 6 | elif target < array[0]: 7 | array = array[pivot + 1:] 8 | idx = binary_search(array, 0, len(array) - 1, target) 9 | if idx != -1: 10 | idx += pivot + 1 11 | return idx 12 | array = array[: pivot] 13 | return binary_search(array, 0, len(array) - 1, target) 14 | 15 | 16 | def find_pivot(array, left_idx, right_idx): 17 | mid_idx = (left_idx + right_idx) // 2 18 | mid = array[mid_idx] 19 | 20 | if mid_idx + 1 >= len(array): 21 | return -1 22 | 23 | if array[mid_idx + 1] < mid: 24 | return mid_idx 25 | elif array[mid_idx - 1] > mid: 26 | return mid_idx - 1 27 | elif array[left_idx] > mid: 28 | return find_pivot(array, left_idx, mid_idx - 1) 29 | return find_pivot(array, mid_idx + 1, right_idx) 30 | 31 | 32 | def binary_search(array, left_idx, right_idx, target): 33 | if left_idx > right_idx: 34 | return -1 35 | 36 | mid_idx = (left_idx + right_idx) // 2 37 | mid = array[mid_idx] 38 | 39 | if mid == target: 40 | return mid_idx 41 | elif mid < target: 42 | return binary_search(array, mid_idx + 1, right_idx, target) 43 | return binary_search(array, left_idx, mid_idx - 1, target) 44 | 45 | 46 | if __name__ == '__main__': 47 | # print(shiftedBinarySearch([1, 2, 3], 4)) 48 | print(shiftedBinarySearch([73, 0, 1, 21, 33, 45, 45, 61, 71, 72], 70)) 49 | -------------------------------------------------------------------------------- /3.5. balanced-brackets.py: -------------------------------------------------------------------------------- 1 | def balancedBrackets(string): 2 | # Write your code here. 3 | stack = [] 4 | for bracket in string: 5 | if len(stack) == 0: 6 | stack.append(bracket) 7 | elif bracket in '()[]{}': 8 | stack.pop(-1) if is_reversed(stack[-1], bracket) else stack.append(bracket) 9 | 10 | return len(stack) == 0 11 | 12 | 13 | def is_reversed(b1, b2): 14 | if b1 == '(' and b2 == ')': 15 | return True 16 | elif b1 == '[' and b2 == ']': 17 | return True 18 | elif b1 == '{' and b2 == '}': 19 | return True 20 | else: 21 | return False 22 | -------------------------------------------------------------------------------- /3.6. validate-bst.py: -------------------------------------------------------------------------------- 1 | def validateBst(node, less_than=float('inf'), greater_than=float('-inf')): 2 | if node is None: 3 | return True 4 | if greater_than <= node.value < less_than: 5 | left_validation = validateBst(node.left, node.value, greater_than) 6 | right_validation = validateBst(node.right, less_than, node.value) 7 | return left_validation and right_validation 8 | return False 9 | -------------------------------------------------------------------------------- /3.7. powerset.py: -------------------------------------------------------------------------------- 1 | def powerset(array): 2 | pass -------------------------------------------------------------------------------- /3.8. min-max-stack.py: -------------------------------------------------------------------------------- 1 | class MinMaxStack: 2 | def __init__(self): 3 | self.values = [] 4 | self.min_max = [[float('inf'), float('-inf')]] 5 | 6 | def pop(self): 7 | self.min_max.pop() 8 | return self.values.pop() 9 | 10 | def push(self, number): 11 | prev_min, prev_max = self.min_max[-1] 12 | new_min, new_max = min(prev_min, number), max(prev_max, number) 13 | self.min_max.append([new_min, new_max]) 14 | self.values.append(number) 15 | 16 | def peek(self): return self.values[-1] if len(self.values) > 0 else None 17 | 18 | def getMin(self): return self.min_max[-1][0] 19 | 20 | def getMax(self): return self.min_max[-1][1] 21 | -------------------------------------------------------------------------------- /3.9. longest-palindromic-substring.py: -------------------------------------------------------------------------------- 1 | # def longestPalindromicSubstring(string): 2 | # # Write your code here. 3 | # max_palindrome = '' 4 | # for i in range(len(string)): 5 | # for j in range(len(string)): 6 | # w = string[i: len(string) - j] 7 | # if is_palindrome(w): 8 | # if len(w) > len(max_palindrome): 9 | # max_palindrome = w 10 | # 11 | # return max_palindrome 12 | # 13 | # 14 | # def is_palindrome(string): 15 | # for i in range(len(string) // 2): 16 | # if string[i] != string[-i - 1]: 17 | # return False 18 | # return True 19 | 20 | # def is_palindrome2(string): 21 | # if len(string) <= 1: 22 | # return True 23 | # 24 | # stack = [] 25 | # for char in string: 26 | # if len(stack) == 0: 27 | # stack.append(char) 28 | # else: 29 | # if len(stack) > 1 and stack[-2] == char: 30 | # stack.pop() 31 | # stack.pop() 32 | # elif stack[-1] == char: 33 | # stack.pop() 34 | # else: 35 | # stack.append(char) 36 | # return len(stack) == 0 37 | # 38 | 39 | 40 | def longestPalindromicSubstring(string): 41 | # Write your code here. 42 | if len(string) <= 1: 43 | return string 44 | longest = '' 45 | for i, char in enumerate(string): 46 | pass 47 | 48 | 49 | def largest_palindrome_from_center(string, center_idx): 50 | if len(string) == 1: 51 | return string 52 | if center_idx == len(string) - 1: 53 | if string[center_idx - 1] == string[center_idx]: 54 | return string[center_idx - 1: center_idx + 1] 55 | else: 56 | return string[center_idx] 57 | elif center_idx == 0: 58 | if string[center_idx + 1] == string[center_idx]: 59 | return string[center_idx: center_idx + 1] 60 | else: 61 | return string[center_idx] 62 | return search_palindrome(string, center_idx - 1, center_idx + 1) 63 | 64 | 65 | def search_palindrome(string, left_idx, right_idx): 66 | for i in range((left_idx + right_idx) // 2): 67 | left = left_idx - i 68 | right = right_idx + i 69 | 70 | print(left, right) 71 | 72 | if string[left] != string[right]: 73 | odd_palindrome = search_palindrome(string, left_idx - 1, right_idx) 74 | even_palindrome = string[left + 1:right] 75 | return max(even_palindrome, odd_palindrome, key=lambda x: len(x)) 76 | elif right == len(string) - 1 or left <= 0: 77 | return string[left:right + 1] 78 | return '' 79 | 80 | 81 | if __name__ == '__main__': 82 | # print(largest_palindrome_from_center('aabaa', 2)) 83 | # print(largest_palindrome_from_center('aba', 1)) 84 | # print(largest_palindrome_from_center('abba', 2)) 85 | # print(largest_palindrome_from_center('aaa', 1)) 86 | # print(largest_palindrome_from_center('aa', 1)) 87 | print(largest_palindrome_from_center('aab', 0)) 88 | --------------------------------------------------------------------------------