├── .gitignore ├── Airport Connections.py ├── All Kinds Of Node Depths.py ├── Ambigious Measurements.py ├── Apartment Hunting.py ├── Array of Products.py ├── Astar Algorithim.py ├── BST Construction.py ├── BST Traversal.py ├── Balanced Brackets.py ├── Binary Search.py ├── Binary Tree Diameter.py ├── Boggle Board.py ├── Branch Sums.py ├── Breadth-first Search.py ├── Bubble Sort.py ├── Caesar Cypher Encryptor.py ├── Calender Matching.py ├── Class Photos.py ├── Compare Leaf Traversal.py ├── Continous Median.py ├── Count Inversions.py ├── Cycle in Graph.py ├── Depths-First Search.py ├── Detect Arbitrage.py ├── Dijkstra's Algorithm.py ├── Disk Stacking.py ├── Find Closest Value in BST.py ├── Find Kth Largest Value In BST.py ├── Find Loop.py ├── Find Nodes Distance K.py ├── Find Successor.py ├── Find Three Largest Numbers.py ├── First Duplicate Value.py ├── First Non-Repeating Character.py ├── Flatten Binary Tree.py ├── Four Number Sum.py ├── Generate Div Tags.py ├── Generate Document.py ├── Group Anagrams.py ├── Heap Sort.py ├── Height Balanced Binary Tree.py ├── Index Equals Value.py ├── Insertion Sort.py ├── Interweaving Strings.py ├── Invert Binary Tree.py ├── Iterative In-order Traversal.py ├── Kadane's Algorithm.py ├── Knapsack Problem.py ├── Knuth-Morris-Pratt Algorithm.py ├── LRU Cache.py ├── Laptop Rentals.py ├── Largest Range.py ├── Largest Rectangle Under Skyline.py ├── Levenshtein Distance.py ├── Line Through Points.py ├── Linked List Construction.py ├── Linked List Palindrome.py ├── Longest Balanced Substring.py ├── Longest Common Subsequence.py ├── Longest Increasing Subsequence.py ├── Longest Palindromic Substring.py ├── Longest Peak.py ├── Longest String Chain.py ├── Longest Substring Without Duplication.py ├── Lowest Common Manager.py ├── Max Path Sum In Binary Tree.py ├── Max Profit With K Transactions.py ├── Max Subset Sum No Adjacent.py ├── Max Sum Increasing Subsequence.py ├── Maximixie Expression.py ├── Maximum Sum Submatrix.py ├── Merge Linked Lists.py ├── Merge Sort.py ├── Merge Sorted Arrays.py ├── Merging Overlapping Intervals.py ├── Min Heap Construction.py ├── Min Height BST.py ├── Min Max Stack Construction.py ├── Min Number of Coins For Change.py ├── Min Number of Jumps.py ├── Min Rewards.py ├── Minimum Area Rectangle.py ├── Minimum Character For Words.py ├── Minimum Passes of Matrix.py ├── Minimum Waiting Time.py ├── Monotonic Array.py ├── Move Element to the End.py ├── Multi String Search.py ├── Next Greater Element.py ├── Node Depths.py ├── Node Swap.py ├── Non-Attacking Queens.py ├── Non-Constructible Change.py ├── Nth Fibonacci.py ├── Number Of Binary Tree Topoligies.py ├── Number of Ways To Make Change.py ├── Number of Ways To Traverse Graph.py ├── Numbers in PI.py ├── Palindrome Check.py ├── Palindrome Partitioning Min Cuts.py ├── Pattern Matcher.py ├── Permutations.py ├── Phone Number Mnemonics.py ├── Powerset.py ├── Product Sum.py ├── Quick Sort.py ├── Quickselect.py ├── README.md ├── Radix Sort.py ├── Rearrange Linked List.py ├── Reconstruct BST.py ├── Rectangle Mania.py ├── Remove Duplicates From a Linked List.py ├── Remove Kth Node From End.py ├── Remove islands.py ├── Reverse Linked List.py ├── Reverse Words in String.py ├── Right Sibling Tree.py ├── Right Smaller Than.py ├── River Sizes.py ├── Run-Length Encoding.py ├── Same BSTs.py ├── Search For Range.py ├── Search in Sorted Matrix.py ├── Selection Sort.py ├── Shift Linked List.py ├── Shifted Binary Search.py ├── Shorten Path.py ├── Single Cycle Check.py ├── Smallest Difference.py ├── Smallest Substring Containing.py ├── Solve Sudoku.py ├── Sort K-Sorted Array.py ├── Sort Stack.py ├── Sorted Square Array.py ├── Spiral Traverse.py ├── Square of Zeroes.py ├── Staircase Traversal.py ├── Subarray Sort.py ├── Suffix Trie Construction.py ├── Sum of Linked Lists.py ├── Sunset Views.py ├── Tandem Bicycle.py ├── Task Assignment.py ├── Three Number Sort.py ├── Three Number Sum.py ├── Topological Sort.py ├── Tournament Winner.py ├── Two Number Sum.py ├── Two-Edge-Connected Graph.py ├── Underscorify Substring.py ├── Valid IP Addresses.py ├── Valid Starting City.py ├── Validate BST.py ├── Validate Subsequence.py ├── Validate Three Nodes.py ├── Water Area.py ├── Waterfall Streams.py ├── Youngest Common Ancestor.py ├── Zigzag Traverse.py └── Zip Linked List.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | # dotenv 4 | .env 5 | 6 | # virtualenv 7 | .venv 8 | venv/ 9 | ENV/ 10 | 11 | # Installer logs 12 | pip-log.txt 13 | pip-delete-this-directory.txt 14 | 15 | # Unit test / coverage reports 16 | htmlcov/ 17 | .tox/ 18 | .coverage 19 | .coverage.* 20 | .cache 21 | nosetests.xml 22 | coverage.xml 23 | *.cover 24 | .hypothesis/ 25 | 26 | # Byte-compiled / optimized / DLL files 27 | __pycache__/ 28 | *.py[cod] 29 | *$py.class 30 | 31 | # C extensions 32 | *.so 33 | 34 | # Distribution / packaging 35 | .Python 36 | env/ 37 | build/ 38 | develop-eggs/ 39 | dist/ 40 | downloads/ 41 | eggs/ 42 | .eggs/ 43 | lib/ 44 | lib64/ 45 | parts/ 46 | sdist/ 47 | var/ 48 | wheels/ 49 | *.egg-info/ 50 | .installed.cfg 51 | *.egg -------------------------------------------------------------------------------- /Array of Products.py: -------------------------------------------------------------------------------- 1 | """ 2 | Array of Products 3 | 4 | Write a function that takes in a non-empty array of integers and returns an array of the 5 | same length, where each element in the output array is equal to the product of every 6 | other number in the input array. 7 | 8 | In other words,the value at output[i] is equal to the product of every number in the 9 | input array other than input[i]. 10 | 11 | Note that you're expected to solve this problem without using division 12 | 13 | Sample Input 14 | array = [5, 1, 4, 2] 15 | 16 | Sample Output 17 | [8, 40, 10, 20] 18 | 19 | // 8 is equal to 1 x 4 x 2 20 | // 40 is equal to 5 x 4 x 2 21 | // 10 is equal to 5 x 1 x 2 22 | // 20 is equal to 5 x 1 x 4 23 | 24 | """ 25 | 26 | 27 | # SOLUTION 1 28 | 29 | # O(n^2) time | O(n) space where n is the length of array. 30 | def arrayOfProducts(array): 31 | products = [1 for _ in range(len(array))] 32 | counter = 0 33 | for i in range(len(array)): 34 | runningProduct = 1 35 | for j in range(len(array)): 36 | if i != j: 37 | runningProduct *= array[j] 38 | 39 | products[i] = runningProduct 40 | counter += 1 41 | 42 | return products 43 | 44 | 45 | # SOLUTION 2 46 | 47 | # O(n) time | O(n) space where n is the length of array. 48 | def arrayOfProducts(array): 49 | products = [1 for _ in range(len(array))] 50 | 51 | leftRunningProduct = 1 52 | for i in range(len(array)): 53 | products[i] = leftRunningProduct 54 | leftRunningProduct *= array[i] 55 | 56 | rightRunningProduct = 1 57 | for i in reversed(range(len(array))): 58 | products[i] *= rightRunningProduct 59 | rightRunningProduct *= array[i] 60 | 61 | return products 62 | -------------------------------------------------------------------------------- /BST Traversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | BST Traversal 3 | 4 | Write three functions that take in a Binary Search Tree(BST) and an empty array, traverse the BST, 5 | add its nodes' values to the input array, and return that array.The three functions should traverse the 6 | BST using the in-order,pre-order,and post-order tree-traversal techniques respectively. 7 | 8 | If you're unfamiliar with tree-traversal techniques we recommend watching the conceptual overview 9 | section of this question's video explanation before starting to code. 10 | 11 | Each BST node has an integer value, a left child node,and a right child node. A node is said to be a 12 | valid BST node if and only if it satisfies the BST property : its value is strictly greater than the 13 | values of every node to its left ;its value is less than or equal to the values of every node to its 14 | right and its children nodes are either valid BST nodes themselves or None/ null. 15 | 16 | Sample Input 17 | 18 | 10 19 | / \ 20 | 5 15 21 | / \ \ 22 | 2 5 22 23 | / 24 | 1 25 | array = [] 26 | 27 | Sample Output 28 | inOrderTraverse : [1, 2, 5, 5, 10, 15, 22] // where the array is the input array 29 | PreOrderTraverse : [10, 5, 2, 1, 5, 15, 22] // where the array is the input array 30 | postOrderTraverse : [1, 2, 5, 5, 22, 15, 10] // where the array is the input array 31 | 32 | 33 | """ 34 | # SOLUTION 1 35 | 36 | # O(n) time | O(n) space 37 | def inOrderTraverse(tree, array): 38 | if tree is None: 39 | return 40 | inOrderTraverse(tree.left, array) 41 | array.append(tree.value) 42 | inOrderTraverse(tree.right, array) 43 | 44 | return array 45 | 46 | # O(n) time | O(n) space 47 | def preOrderTraverse(tree, array): 48 | if tree is None: 49 | return 50 | array.append(tree.value) 51 | preOrderTraverse(tree.left, array) 52 | preOrderTraverse(tree.right, array) 53 | 54 | return array 55 | 56 | # O(n) time | O(n) space 57 | def postOrderTraverse(tree, array): 58 | if tree is None: 59 | return 60 | postOrderTraverse(tree.left, array) 61 | postOrderTraverse(tree.right, array) 62 | array.append(tree.value) 63 | 64 | return array 65 | 66 | 67 | -------------------------------------------------------------------------------- /Balanced Brackets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Balanced Brackets 3 | 4 | Write a function that takes in a string made up of brackets( (, [, {, ) , ], and } ) 5 | and other optional characters.The function should return a boolean representing whether 6 | the string is balanced with regards to brackets. 7 | 8 | A string is said to be balanced if it has as many opening brackets of a certain type as 9 | it has closing brackets of that type and if no bracket is unmatched.Note that an opening 10 | bracket can't match a corresponding closing bracket that comes before it and similarly, a 11 | closing bracket can't match a corresponding opening bracket that comes after it.Also , 12 | brackets can't match a corresponding opening bracket that comes after it .Also, brackets 13 | can't overlap each other as in [(]). 14 | 15 | Sample Input 16 | string = '([])(){}(())()()' 17 | 18 | Sample Output 19 | true // it's balanced 20 | """ 21 | 22 | # SOLUTION 1 23 | 24 | # O(n) time | O(n) space 25 | def balancedBrackets(string): 26 | openingBrackets = '([{' 27 | closingBrackets = ')]}' 28 | matchingBrackets = {')': '(', ']': '[', "}": "{"} 29 | stack = [] 30 | for char in string: 31 | if char in openingBrackets: 32 | stack.append(char) 33 | elif char in closingBrackets: 34 | if len(stack) == 0: 35 | return False 36 | if stack[-1] == matchingBrackets[char]: 37 | stack.pop() 38 | else: 39 | return False 40 | return len(stack) == 0 41 | 42 | -------------------------------------------------------------------------------- /Binary Search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Binary Search 3 | 4 | Write a function that takes in a sorted array of the integers as well as a target. 5 | The function should use the Binary search algorithm to determine if the target integer 6 | is contained in the array and should return its index if it is ,otherwise -1. 7 | 8 | Sample Input 9 | array = [0, 1, 21, 33, 45, 61, 71, 72, 72, 73] 10 | target = 33 11 | 12 | Sample Output 13 | 3 14 | """ 15 | 16 | # SOLUTION 1 17 | 18 | # Running time O(logn) | space O(1) 19 | def binarySearch1(array, target): 20 | left = 0 21 | right = len(array) - 1 22 | while left <= right: # or not (left > right) 23 | mid = (left + right) // 2 24 | if target > array[mid]: 25 | left = mid + 1 26 | elif target < array[mid]: 27 | right = mid - 1 28 | else: 29 | return mid 30 | return -1 31 | 32 | # SOLUTION 2 33 | 34 | # time O(logn) | space O(logn) 35 | def binarySearch(array, target): 36 | return binarySearchHelper(array, target, 0, len(array) - 1) 37 | 38 | 39 | def binarySearchHelper(array, target, left, right): 40 | if left > right: 41 | return -1 42 | mid = (left + right) // 2 43 | if target > array[mid]: 44 | return binarySearchHelper(array, target, mid + 1, right) 45 | elif target < array[mid]: 46 | return binarySearchHelper(array, target, left, mid - 1) 47 | else: 48 | return mid 49 | 50 | -------------------------------------------------------------------------------- /Binary Tree Diameter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Binary Tree Diameter 3 | 4 | Write a function that takes in a Binary Tree and returns its diameter.The diameter of a binary 5 | tree is defined as the length of its longest path,,even if that path doesn't pass through the 6 | root of the tree. 7 | 8 | A path is a collection of connected nodes in a tree,where no node is connected to more than 9 | two other nodes. The length of a path is the number of edges between the paths's first node 10 | and its last node. 11 | 12 | Each BinaryTree node node has an integer value, a left child node, and a right child node.Children 13 | nodes can either be BinaryTree nodes themselves or None/ null. 14 | 15 | Sample Input 16 | 1 17 | / \ 18 | 3 2 19 | / \ 20 | 7 4 21 | / \ 22 | 8 5 23 | / \ 24 | 9 6 25 | 26 | Sample Output 27 | 6 // 9 -> 8 -> 7 -> 3 ->4 -> 5 -> 6 28 | // There are 6 edges between the first node and the last node of this tree's longest path. 29 | 30 | """ 31 | 32 | # SOLUTION 1 33 | 34 | # O(n) time | O(h) space where n is the number of nodes in the binary tree 35 | # and h is the height of the Binary tree 36 | 37 | class BinaryTree: 38 | def __init__(self, value, left=None, right=None): 39 | self.value = value 40 | self.left = left 41 | self.right = right 42 | 43 | 44 | def binaryTreeDiameter(tree): 45 | return getTreeInfo(tree).diameter 46 | 47 | 48 | def getTreeInfo(tree): 49 | if tree is None: 50 | return TreeInfo(0, 0) 51 | 52 | leftTreeInfo = getTreeInfo(tree.left) 53 | rightTreeInfo = getTreeInfo(tree.right) 54 | 55 | longestPathThroughRoot = leftTreeInfo.height + rightTreeInfo.height 56 | maxDiameterSoFar = max(leftTreeInfo.diameter, rightTreeInfo.diameter) 57 | currentDiameter = max(longestPathThroughRoot, maxDiameterSoFar) 58 | currentHeight = 1 + max(leftTreeInfo.height, rightTreeInfo.height) 59 | 60 | return TreeInfo(currentDiameter, currentHeight) 61 | 62 | 63 | class TreeInfo: 64 | def __init__(self, diameter, height): 65 | self.diameter = diameter 66 | self.height = height 67 | -------------------------------------------------------------------------------- /Branch Sums.py: -------------------------------------------------------------------------------- 1 | """ 2 | Branch Sums 3 | 4 | Write a function that takes in a Binary Tree and returns a list of its branch sums ordered 5 | from leftmost branch sum to rightmost branch sum. 6 | 7 | A branch sum is the sum of all values in a Binary Tree branch.A Binary Tree branch is a 8 | path of nodes in a tree that starts at the root node and ends at any leaf node. 9 | 10 | Each BinaryTree node has an integer value, a left child node and a right child node. 11 | Children nodes can either be BinaryTree nodes themselves or None / null. 12 | 13 | Sample Input 14 | tree = 1 15 | / \ 16 | 2 3 17 | / \ / \ 18 | 4 5 6 7 19 | / \ / 20 | 8 9 10 21 | 22 | Sample Output 23 | [15, 16, 18, 10, 11] 24 | // 15 == 1 + 2 + 4 + 8 25 | // 16 == 1 + 2 + 4 + 9 26 | // 18 == 1 + 2 + 5 + 10 27 | // 10 == 1 + 3 + 6 28 | // 11 == 1 + 3 + 7 29 | 30 | """ 31 | # SOLUTION 1 32 | 33 | # Running time O(N) space O(N), where N represents the number of Nodes 34 | class BinaryTree: 35 | def __init__(self, value): 36 | self.value = value 37 | self.left = None 38 | self.right = None 39 | 40 | 41 | def branchSums(root): 42 | sums = [] 43 | calcbranchsums(root, 0, sums) 44 | return sums 45 | 46 | 47 | def calcbranchsums(node, runningsums, sums): 48 | if node is None: 49 | return 50 | newrunningsums = runningsums + node.value 51 | 52 | if node.left is None and node.right is None: 53 | sums.append(newrunningsums) 54 | 55 | calcbranchsums(node.left, newrunningsums, sums) 56 | calcbranchsums(node.right, newrunningsums, sums) 57 | -------------------------------------------------------------------------------- /Breadth-first Search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Breadth-first Search 3 | 4 | You're given a Node class that has a name and an array of optional children 5 | nodes.When put together,nodes form an acyclic tree-like structure. 6 | 7 | Implement the breadthFirstSearch method on the Node class,which takes in 8 | an empty array,traverses the tree using the Breadth-first Search approach 9 | (specifically navigating the tree from left to right), stores all of the nodes 10 | names in the input array,and returns it. 11 | 12 | Sample Input 13 | 14 | graph = A 15 | / | \ 16 | B C D 17 | / \ / \ 18 | E F G H 19 | /\ \ 20 | I J K 21 | 22 | Sample Output 23 | ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K'] 24 | """ 25 | 26 | 27 | # SOLUTION 1 28 | 29 | class Node: 30 | def __init__(self, name): 31 | self.children = [] 32 | self.name = name 33 | 34 | def addChild(self, name): 35 | self.children.append(Node(name)) 36 | return self 37 | 38 | # O(v + e) time | O(v) space 39 | def breadthFirstSearch(self, array): 40 | queue = [self] 41 | while len(queue) > 0: 42 | current = queue.pop(0) 43 | array.append(current.name) 44 | for child in current.children: 45 | queue.append(child) 46 | 47 | return array 48 | 49 | -------------------------------------------------------------------------------- /Bubble Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Bubble Sort 3 | 4 | Write a function that takes in an array of integers and returns a sorted version of that 5 | array.Use the Bubble Sort algorithm to sort the array. 6 | 7 | Sample Input 8 | array = [8, 5, 2, 9, 5, 6, 3] 9 | 10 | Sample Output 11 | [2, 3, 5, 5, 6, 8, 9] 12 | """ 13 | 14 | # SOLUTION 1 15 | 16 | # Running time O(n^2) space 0(1) 17 | def bubbleSort(array): 18 | issorted = False 19 | counter = 0 20 | while not issorted: 21 | issorted = True 22 | for j in range(0, len(array) - counter - 1): 23 | if array[j] > array[j + 1]: 24 | array[j], array[j + 1] = array[j + 1], array[j] 25 | issorted = False 26 | 27 | counter += 1 28 | return array 29 | 30 | # SOLUTION 2 31 | 32 | # Running time O(n^2) space 0(1) 33 | def bubbleSort(array): 34 | # run loops two times: one for walking through the array 35 | # and the other for comparison 36 | for i in range(len(array)): 37 | # largest element is placed at the end no need to go to the end 38 | for j in range(0, len(array) - i - 1): 39 | if array[j] > array[j + 1]: 40 | array[j], array[j + 1] = array[j + 1], array[j] 41 | return array 42 | 43 | 44 | # SOLUTION 3 45 | # Running time O(n^2) space 0(1) 46 | def bubbleSort(array): 47 | for i in range(len(array)): 48 | is_sorted = True 49 | for j in range(0, len(array) - i - 1): 50 | if array[j] > array[j + 1]: 51 | array[j], array[j + 1] = array[j + 1], array[j] 52 | is_sorted = False 53 | if is_sorted: 54 | break 55 | return array 56 | 57 | 58 | -------------------------------------------------------------------------------- /Caesar Cypher Encryptor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Caesar Cypher Encryptor 3 | 4 | Give a non-empty string of lowercase letters and a non negative integer representing 5 | a key,write a function that returns a new string obtained by shifting every letter in 6 | the input string by k positions in the alphabet,where k is the key. 7 | 8 | Note that the letters should "wrap" around the alphabet,in other words,the letter z 9 | shifted by one returns the letter a. 10 | 11 | Sample Input 12 | string = 'xyz' 13 | key = 2 14 | 15 | Sample Output 16 | "zab" 17 | 18 | """ 19 | 20 | # SOLUTION 1 21 | 22 | # Running time O(n)| space 0(n) 23 | def caesarCipherEncryptor2(string, key): 24 | newString = [] 25 | key = key % 26 26 | for letter in string: 27 | newLettercode = ord(letter) + key 28 | if newLettercode <= 122: 29 | newString.append(chr(newLettercode)) 30 | else: 31 | newString.append(chr(96 + newLettercode % 122)) 32 | 33 | return ''.join(newString) 34 | 35 | 36 | # SOLUTION 2 37 | 38 | # Running time O(n)| space 0(n) 39 | def caesarCipherEncryptor(string, key): 40 | alphabets = list('abcdefghijklmnopqrstuvwxyz') 41 | key = key % 26 42 | newString = [] 43 | for letter in string: 44 | newlettercode = (ord(letter) - 97) + key 45 | idx = newlettercode % 26 46 | newString.append(alphabets[idx]) 47 | 48 | return ''.join(newString) 49 | 50 | # SOLUTION 3 51 | 52 | # Running time O(n)| space 0(n) 53 | def caesarCipherEncryptor(string, key): 54 | newLetters = [] 55 | newkey = key % 26 56 | alphabets = list('abcdefghijklmnopqrstuvwxyz') 57 | 58 | for letter in string: 59 | newLettercode = alphabets.index(letter) + key 60 | newLetters.append(alphabets[newLettercode % 26]) 61 | 62 | return ''.join(newLetters) 63 | 64 | -------------------------------------------------------------------------------- /Class Photos.py: -------------------------------------------------------------------------------- 1 | """ 2 | Class Photos 3 | 4 | It's photo day at the school,and you're the photographer assigned to take class photos. 5 | The class that you'll be photographing has an even number of students,and all these 6 | students are wearing red or blue shirts.In fact,exactly half of the class is wearing 7 | red shirts, and the other half is wearing blue shirts.You're responsible for arranging 8 | the students in two rows before taking the photo.each row should contain the same number 9 | of the students and should adhere to the following guidelines: 10 | 11 | - All students wearing red shirts must be in the same row. 12 | - All students wearing blue shirts must be in the same row. 13 | - Each students in the back row must be strictly taller than 14 | the student directly in front of them in the front row. 15 | 16 | You're given two input arrays: one containing the heights of all the students with 17 | red shirts and another one containing the heights of all the students with blue shirts. 18 | These arrays will always have the same Length and each height will a positive integer 19 | 20 | Write a function that returns whether or not a class photo that follows the stated 21 | guidelines can be taken. 22 | 23 | Note: You can assume that each class has at least 2 students. 24 | 25 | Sample Input 26 | 27 | redShirtsHeights = [5, 8, 1, 3, 4] 28 | blueShirtHeights = [6, 9, 2, 4, 5] 29 | 30 | Sample Output 31 | true 32 | """ 33 | 34 | # SOLUTION 1 35 | 36 | # O(nlogn) time | O(1) space where n is the length of array. 37 | def classPhotos(redShirtHeights, blueShirtHeights): 38 | redShirtHeights.sort(reverse=True) 39 | blueShirtHeights.sort(reverse=True) 40 | 41 | shirtColourInFirstRow = 'RED' if redShirtHeights[0] < blueShirtHeights[0] else "BLUE" 42 | for idx in range(len(blueShirtHeights)): 43 | redShirtHeight = redShirtHeights[idx] 44 | blueShirtHeight = blueShirtHeights[idx] 45 | 46 | if shirtColourInFirstRow == 'RED': 47 | if redShirtHeight >= blueShirtHeight: 48 | return False 49 | else: 50 | if blueShirtHeight >= redShirtHeight: 51 | return False 52 | 53 | return True 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Count Inversions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Count Inversions ☆ 3 | Write a function that takes in an array of integers and returns the number of inversions in the array. An inversion 4 | occurs if for any valid indices i and j. i array[j] . 5 | For example, given array = [3, 4, 1, 2] , there are 4 inversions. The following pairs of indices represent inversions: 6 | [0, 2], [0, 3], [1, 2], [1, 3] 7 | Intuitively, the number of inversions is a measure of how unsorted the array is. 8 | 9 | Sample Input 10 | array = [2, 3, 3, 1, 9, 5, 6] 11 | 12 | Sample Output 13 | 5 14 | // The following pairs of indices represent inversions: 15 | // [0, 3], [1, 3], [2, 3], [4, 5], [4, 6] 16 | 17 | """ 18 | 19 | # SOLUTION 1 20 | 21 | # O(nlogn) time | O(n) space where n is the length of the array 22 | def countInversions (array): 23 | return countSubArrayInversions (array, 0, len(array)) 24 | 25 | def countSubArrayInversions (array, start, end): 26 | if end - start <= 1: 27 | return 0 28 | middle = start + (end - start) // 2 29 | leftInversions = countSubArrayInversions (array, start, middle) 30 | rightInversions = countSubArrayInversions(array, middle, end) 31 | mergedArrayInversions = mergeSortAndCountInversions(array, start, middle, end) 32 | return leftInversions + rightInversions + mergedArrayInversions 33 | 34 | def mergeSortAndCountInversions (array, start, middle, end): 35 | sortedArray = [] 36 | left = start 37 | right = middle 38 | inversions = 0 39 | 40 | while left < middle and right < end: 41 | if array[left] <= array[right]: 42 | sortedArray.append(array[left]) 43 | left += 1 44 | else: 45 | inversions += middle - left 46 | sortedArray.append(array[right]) 47 | right += 1 48 | sortedArray += array[left:middle] + array[right:end] 49 | for idx, num in enumerate(sortedArray): 50 | array[start + idx] = num 51 | 52 | return inversions 53 | -------------------------------------------------------------------------------- /Depths-First Search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Depth-First Search 3 | 4 | You're given Node class that has a name and and an array of optional children nodes. 5 | When put together,nodes form an acyclic tree-like structure. 6 | 7 | Implement the depthFirstSearch method on the Node class,which takes in an empty array, 8 | traverses the tree using the Depth-first Search approach (specifically navigating the 9 | tree from left to right),stores all of the nodes' names in the input array, and return 10 | it. 11 | 12 | Sample Input 13 | tree = A 14 | / | \ 15 | B C D 16 | / \ / \ 17 | E F G H 18 | / \ \ 19 | I J K 20 | 21 | Sample Output 22 | ['A','B','E','F','I','J','C','D','G','K','H'] 23 | """ 24 | 25 | # SOLUTION 1 26 | 27 | class Node: 28 | def __init__(self, name): 29 | self.children = [] 30 | self.name = name 31 | 32 | def addChild(self, name): 33 | self.children.append(Node(name)) 34 | return self 35 | 36 | # Running time O(V+E) space O(V), V-vertices E-edges 37 | def depthFirstSearch(self, array): 38 | array.append(self.name) 39 | childs = self.children 40 | if childs is None: 41 | return 42 | for child in childs: 43 | child.depthFirstSearch(array) 44 | 45 | return array 46 | -------------------------------------------------------------------------------- /Disk Stacking.py: -------------------------------------------------------------------------------- 1 | """ 2 | Disk Stacking 3 | You're given a non-empty array of arrays where each subarray holds three integers and represents a disk. These integers 4 | denote each disk's width, 5 | depth, and height, respectively. Your goal is to stack up the disks and to maximize the total height of the stack. A 6 | disk must have a strictly smaller width, depth, and height than any other disk below it. 7 | 8 | Write a function that returns an array of the disks in the final stack, starting with the top disk and ending with the 9 | bottom disk. Note that you can't rotate disks; in other words, the integers in each subarray must represent [width, 10 | depth, height] at all times. 11 | 12 | You can assume that there will only be one stack with the greatest total height. 13 | 14 | Sample Input 15 | disks = [[2, 1, 2], [3, 2, 3], [2, 2, 8], [2, 3, 4], [1, 3, 1], [4, 4, 5]] 16 | 17 | Sample Output 18 | 19 | [[2, 1, 2], (3, 2, 3], [4, 4, 5]] 20 | 21 | // 10 (2 + 3 + 5) is the tallest height we can get by 22 | // stacking disks following the rules laid out above. 23 | 24 | """ 25 | 26 | # SOLUTION 1 27 | 28 | # O(n^2) time | O(n) space 29 | def diskStacking(disks): 30 | disks.sort(key=lambda disk: disk[2]) 31 | heights = [disk[2] for disk in disks] 32 | sequences = [None for disk in disks] 33 | maxHeightIdx = 0 34 | for i in range(1, len(disks)): 35 | currentDisk = disks[i] 36 | for j in range(0, i): 37 | otherDisk = disks[j] 38 | if areValidDimensions(otherDisk, currentDisk): 39 | if heights[i] <= currentDisk[2] + heights[j]: 40 | heights[i] = currentDisk[2] + heights[j] 41 | sequences[i] = j 42 | if heights[i] >= heights[maxHeightIdx]: 43 | maxHeightIdx = i 44 | 45 | return buildSequence(disks, sequences, maxHeightIdx) 46 | 47 | 48 | def areValidDimensions(o, c): 49 | return o[0] < c[0] and o[1] < c[1] and o[2] < c[2] 50 | 51 | 52 | def buildSequence(array, sequences, currentIdx): 53 | sequence = [] 54 | while currentIdx is not None: 55 | sequence.append(array[currentIdx]) 56 | currentIdx = sequences[currentIdx] 57 | return list(reversed(sequence)) 58 | -------------------------------------------------------------------------------- /Find Closest Value in BST.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find the closest Value in BST 3 | 4 | Write a function that takes in a Binary Search Tree(BST) and a target integer value 5 | and returns the closest value to that target value contained in the BST. 6 | 7 | You can assume that there will only be one closest value. 8 | 9 | Each BST node has an integer value, a left child node, and a right child node. 10 | A node is said to be a valid BST node if and only if it satisfies the BST property: 11 | Its value is strictly greater than the values of every node to its left; its value 12 | is less than or equal to the values of every node to its right and its children nodes 13 | are either valid BST nodes themselves or None / null. 14 | 15 | Sample Input 16 | tree = 10 17 | / \ 18 | 5 15 19 | / \ / \ 20 | 2 5 13 22 21 | / \ 22 | 1 14 23 | 24 | target = 12 25 | 26 | Sample Output 27 | 13 28 | """ 29 | 30 | # SOLUTION 1 31 | 32 | # Using Recursion 33 | # time - O(log(n)) | space - O(log(n)) because of frames in call stack 34 | def findclosestvalueSub(tree, target): 35 | return closestvaluebstfinder(tree, target, float('inf')) 36 | 37 | 38 | def closestvaluebstfinder(tree, target, closest): 39 | if tree is None: 40 | return closest 41 | if abs(target - closest) > abs(target - tree.value): 42 | closest = tree.value 43 | if target < tree.value: 44 | return closestvaluebstfinder(tree.left, target, closest) 45 | elif target > tree.value: 46 | return closestvaluebstfinder(tree.right, target, closest) 47 | else: 48 | return closest 49 | 50 | 51 | # SOLUTION 2 52 | 53 | # Using while Loop 54 | # time - O(log(n)) | space - O(1) 55 | def findclosestvalueBst(tree, target): 56 | return closestvalueBst(tree, target, float('inf')) 57 | 58 | 59 | def closestvalueBst(tree, target, closest): 60 | current_node = tree 61 | while current_node is not None: 62 | if abs(target - closest) > abs(target - current_node.value): 63 | closest = current_node.value 64 | if target < current_node.value: 65 | current_node = current_node.left 66 | elif target > current_node.value: 67 | current_node = current_node.right 68 | else: 69 | break 70 | return closest 71 | -------------------------------------------------------------------------------- /Find Loop.py: -------------------------------------------------------------------------------- 1 | """ 2 | Find Loop ☆ 3 | Write a function that takes in the head of a Singly Linked List that contains a loop (in other words, 4 | the list's tail node points to some node in the list instead of None / null ). The function should return the node 5 | (the actual node-not just its value) from which the loop originates in constant space. 6 | 7 | Each LinkedList node has an integer value as well as a next node pointing to the next node in the list. 8 | 9 | Sample Input 10 | head = 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 // the head node with value 0 11 | ^ v 12 | 9 <- 8 <- 7 13 | Sample Output 14 | 4 -> 5 -> 6 // the node with value 4 15 | ^ V 16 | 9 <- 8 - 7 17 | 18 | """ 19 | 20 | # SOLUTION 1 21 | 22 | class LinkedList: 23 | def __init__(self, value): 24 | self.value = value 25 | self.next = None 26 | 27 | # O(n) time | 0(1) space 28 | def findLoop(head): 29 | first = head.next 30 | second = head.next.next 31 | while first != second: 32 | first = first.next 33 | second = second.next.next 34 | first = head 35 | while first != second: 36 | first = first.next 37 | second = second.next 38 | return first 39 | 40 | -------------------------------------------------------------------------------- /Find Three Largest Numbers.py: -------------------------------------------------------------------------------- 1 | """" 2 | Find Three Largest Numbers 3 | 4 | Write a function that takes in an array of at least three integers and ,without 5 | sorting the input array,returns a sorted array of the three largest integers in 6 | the input array. 7 | 8 | The function should return duplicate integers if necessary; for example, it should 9 | return [ 10, 10, 12] for an input array of [10, 5, 9, 10, 12] 10 | 11 | Sample Input 12 | array = [141, 1, 17, -7, -17, -27, 18, 541, 8, 7, 7] 13 | 14 | Sample Output 15 | [18, 141, 541] 16 | """ 17 | 18 | # SOLUTION 1 19 | 20 | # Running time O(n^2) because of list.remove() space O(1) 21 | def findThreeLargestNumbers(array): 22 | # Write your code here. 23 | answ = [None, None, None] 24 | for i in range(2, -1, -1): 25 | res = max(array) 26 | answ[i] = res 27 | array.remove(res) 28 | return answ 29 | 30 | 31 | # SOLUTION 2 32 | 33 | # Running time O(n) space O(1) 34 | def findThreeLargestNumbers(array): 35 | res = [None, None, None] 36 | for elem in array: 37 | update(res, elem) 38 | return res 39 | 40 | 41 | def update(res, elem): 42 | if res[2] is None or elem > res[2]: 43 | shiftupdate(res, elem, 2) 44 | elif res[1] is None or elem > res[1]: 45 | shiftupdate(res, elem, 1) 46 | elif res[0] is None or elem > res[0]: 47 | shiftupdate(res, elem, 0) 48 | 49 | 50 | def shiftupdate(res, elem, indx): 51 | # eg res =[x,y,z] and updating z = num 52 | # res = [y,z,num] 53 | 54 | for i in range(indx + 1): 55 | if i == indx: 56 | res[i] = elem 57 | else: 58 | res[i] = res[i + 1] 59 | 60 | 61 | # SOLUTION 3 62 | 63 | # Running time O(n) space O(1) 64 | def findThreeLargestNumbers(array): 65 | answ = [None, None, None] 66 | for num in array: 67 | check(answ, num) 68 | return answ 69 | 70 | 71 | def check(answ, num): 72 | # All values are None 73 | if answ[0] == answ[1] == answ[2] is None: 74 | answ[2] = num 75 | # Two values are None 76 | elif answ[0] == answ[1] is None: 77 | if num > answ[2]: 78 | answ[1], answ[2] = answ[2], num 79 | else: 80 | answ[1] = num 81 | # One Value is None 82 | elif answ[0] is None: 83 | if num > answ[2]: 84 | answ[0], answ[1], answ[2] = answ[1], answ[2], num 85 | elif num > answ[1]: 86 | answ[0], answ[1] = answ[1], num 87 | else: 88 | answ[0] = num 89 | 90 | # No Value is None 91 | else: 92 | if num > answ[2]: 93 | answ[0], answ[1], answ[2] = answ[1], answ[2], num 94 | elif num > answ[1]: 95 | answ[0], answ[1] = answ[1], num 96 | 97 | elif num > answ[0]: 98 | answ[0] = num 99 | -------------------------------------------------------------------------------- /First Duplicate Value.py: -------------------------------------------------------------------------------- 1 | """ 2 | First Duplicate Value 3 | 4 | Given an array of integers between 1 and n, inclusive,where n is the length of the array,write a function 5 | that returns the first integer that appears more than once(when the array is read from left to right). 6 | 7 | In other words, out of all the integers that might occur more than once in the input array, your function 8 | should return the one whose first duplicate value has the minimum index. 9 | 10 | If no integer appears more than once ,your function should return -1. 11 | 12 | Note that you're allowed to mutate the input array. 13 | 14 | Sample Input #1 15 | array = [2, 1, 5, 2, 3, 3, 4] 16 | 17 | Sample Output #1 18 | 2 // 2 is the first integer that appears more than once. 19 | // 3 also appears more than once, but the second 3 appears after the second 2. 20 | 21 | Sample Input #2 22 | array = [2, 1, 5, 3, 3, 2, 4] 23 | 24 | Sample Output #2 25 | 3 // 3 is the first integer that appears more than once. 26 | // 2 also appears more than once, but the second 2 appears after the second 3. 27 | 28 | 29 | 30 | """ 31 | 32 | # SOLUTION 1 33 | 34 | # O(n) time | O(n) | space where n is the length of array 35 | def firstDuplicateValue(array): 36 | duplicates = {} 37 | for num in array: 38 | if num in duplicates: 39 | return num 40 | duplicates[num] = duplicates.get(num, 1) 41 | return -1 42 | 43 | # SOLUTION 2 44 | 45 | # O(n) time | O(n) | space where n is the length of array 46 | def firstDuplicateValue(array): 47 | seen = set() 48 | for value in array: 49 | if value in seen: 50 | return value 51 | seen.add(value) 52 | return -1 53 | 54 | # SOLUTION 3 55 | 56 | # O(n) time | O(1) | space where n is the length of array 57 | def firstDuplicateValue(array): 58 | for value in array: 59 | absValue = abs(value) 60 | if array[absValue - 1] < 0: 61 | return absValue 62 | array[absValue - 1] *= -1 63 | return -1 64 | 65 | # SOLUTION 4 66 | 67 | # O(n^2) time | O(1) | space where n is the length of array 68 | def firstDuplicateValue(array): 69 | minimumSecondIndex = len(array) 70 | for i in range(len(array)): 71 | value = array[i] 72 | for j in range(i + 1, len(array)): 73 | valueToCompare = array[j] 74 | if value == valueToCompare: 75 | minimumSecondIndex = min(minimumSecondIndex, j) 76 | 77 | if minimumSecondIndex == len(array): 78 | return -1 79 | return array[minimumSecondIndex] 80 | 81 | -------------------------------------------------------------------------------- /First Non-Repeating Character.py: -------------------------------------------------------------------------------- 1 | """ 2 | First Non-Repeating Character 3 | 4 | Write a function that takes in a string of lowercase English-alphabet letters and returns the index of the 5 | string's first non-repeating character. 6 | 7 | The first non-repeating character is the first character in a string that occurs only once. 8 | 9 | If the input string doesn't have any non-repeating characters your function should return -1. 10 | 11 | Sample input 12 | string = "abcdaf" 13 | 14 | Sample Output 15 | 1 // The first non-repeating character is 'b' and is found at index 1. 16 | 17 | """ 18 | 19 | # SOLUTION 1 20 | 21 | # O(n) time | O(1) space - where n is the length of the input string 22 | # The constant space is because the input string only has lowercase 23 | # English-alphabet letters; thus ,our hash table will never have more 24 | # than 26 character frequencies. 25 | def firstNonRepeatingCharacter(string): 26 | letters = countString(string) 27 | 28 | for indx, letter in enumerate(string): 29 | if letters[letter] == 1: 30 | return indx 31 | 32 | return -1 33 | 34 | 35 | def countString(string): 36 | letters = {} 37 | for indx, letter in enumerate(string): 38 | value = letters.setdefault(letter, 0) 39 | letters[letter] = value + 1 40 | return letters 41 | 42 | 43 | # O(n^2) time | O(1) space - where n is the length of the input string 44 | def firstNonRepeatingCharacter(string): 45 | for idx in range(len(string)): 46 | foundDuplicate = False 47 | for idx2 in range(len(string)): 48 | if string[idx] == string[idx2] and idx != idx2: 49 | foundDuplicate = True 50 | if not foundDuplicate: 51 | return idx 52 | return -1 53 | -------------------------------------------------------------------------------- /Four Number Sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | Four Number Sum 3 | Write a function that takes in a non-empty array of distinct integers and an integer representing 4 | a target sum.The function should find all quadruplets in the array that sum up to the target sum 5 | and return a two dimensional array of ll these quadruplets in no particular order. 6 | 7 | If no four numbers sum up to the target sum,the function should return an empty array. 8 | 9 | Sample Input 10 | array = [7, 6, 4, -1, 1, 2] 11 | targetSum = 16 12 | 13 | Sample Output 14 | [[7,6,4,-1], [7,6,1,2]] //the quadruplets could be ordered differently 15 | """ 16 | 17 | # SOLUTION 1 18 | 19 | # Average: O(n^2) time | O(n^2) space 20 | # Worst: O(n^3) time | O(n^2) space 21 | def fourNumberSum(array, targetSum): 22 | allPairsSums = {} 23 | quadruplets = [] 24 | 25 | for i in range(1, len(array) - 1): 26 | for j in range(i + 1, len(array)): 27 | currentSum = array[i] + array[j] 28 | difference = targetSum - currentSum 29 | if difference in allPairsSums: 30 | for pair in allPairsSums[difference]: 31 | quadruplets.append(pair + [array[i], array[j]]) 32 | for k in range(0, i): 33 | currentSum = array[i] + array[k] 34 | if currentSum not in allPairsSums: 35 | allPairsSums[currentSum] = [[array[k], array[i]]] 36 | else: 37 | allPairsSums[currentSum].append([array[k], array[i]]) 38 | return quadruplets 39 | -------------------------------------------------------------------------------- /Generate Div Tags.py: -------------------------------------------------------------------------------- 1 | """ 2 | Generate Div Tags ☆ 3 | Write a function that takes in a positive integer numberOfTags and returns a list of all the valid strings that you can 4 | generate with that number of matched
tags. 5 | A string is valid and contains matched
tags if for every opening tag
, there's a closing tag
6 | that comes after the opening tag and that isn't used as a closing tag for another opening tag. Each output string should 7 | contain exactly numberOfTags opening tags and numberOfTags closing tags.["
", 8 | 9 | For example, given numberOfTags = 2, the valid strings to return would be: "
"] . 10 | Note that the output strings don't need to be in any particular order. 11 | 12 | Sample Input 13 | numberOfTags = 3 14 | 15 | Sample Output 16 | [ 17 | "
", 18 | "
", 19 | "
", 20 | "
", 21 | "
", 22 | ] // The strings could be ordered differently. 23 | 24 | """ 25 | # SOLUTION 1 26 | 27 | # 0((2n)!/((n!((n + 1)!)))) time | ((2n)!/((n!((n + 1)!)))) space - 28 | # where n is the input number 29 | def generateDivTags(numberOfTags): 30 | matchedDivTags = [] 31 | generateDivTagsFromPrefix(numberOfTags, numberOfTags,"", matchedDivTags) 32 | return matchedDivTags 33 | 34 | def generateDivTagsFromPrefix(openingTagsNeeded, closingTagsNeeded, prefix, result): 35 | if openingTagsNeeded > 0: 36 | newPrefix = prefix + "
" 37 | generateDivTagsFromPrefix(openingTagsNeeded - 1, closingTagsNeeded, newPrefix, result) 38 | 39 | if openingTagsNeeded < closingTagsNeeded: 40 | newPrefix = prefix + "
" 41 | generateDivTagsFromPrefix(openingTagsNeeded, closingTagsNeeded - 1, newPrefix, result) 42 | 43 | if closingTagsNeeded == 0: 44 | result.append(prefix) 45 | -------------------------------------------------------------------------------- /Group Anagrams.py: -------------------------------------------------------------------------------- 1 | """ 2 | Group Anagrams 3 | 4 | Write a function that takes in array of strings and groups anagrams together 5 | 6 | Anagrams are strings made up of exactly the same letters,where order doesn't matter. 7 | For example, "cinema" and "iceman" are anagrams; similarly, "foo" and "ofo" are anagrams. 8 | 9 | Your function should return a list of anagram groups in no particular order. 10 | 11 | Sample Input 12 | words = ["yo", "act", "flop", "tac", "foo", "cat", "oy", "olfp"] 13 | 14 | Sample Output 15 | [["yo","oy"], ["flop", "olfp"], ["act", "tac", "cat"], ["foo"]] 16 | 17 | """ 18 | # SOLUTION 1 19 | 20 | # O(w * n * log(n) time | O(n) space - where w is the number of words 21 | # where n is the length of the longest word. 22 | def groupAnagrams(words): 23 | anagrams = {} 24 | for word in words: 25 | sortedword = "".join(sorted(word)) 26 | if sortedword in anagrams: 27 | anagrams[sortedword].append(word) 28 | else: 29 | anagrams[sortedword] = [word] 30 | 31 | return list(anagrams.values()) 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Heap Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Heap Sort ♡ 3 | Write a function that takes in an array of integers and returns a sorted version of that array. Use the Heap Sort algorithm 4 | to sort the array. 5 | If you're unfamiliar with Heap Sort, we recommend watching the Conceptual Overview section of this question's video 6 | explanation before starting to code. 7 | 8 | Sample Input 9 | array = [8, 5, 2, 9, 5, 6, 3] 10 | 11 | Sample Output 12 | [2, 3, 5, 5, 6, 8, 9] 13 | 14 | """ 15 | # SOLUTION 1 16 | 17 | # Best: O(nlog(n)) time | 0(1) space 18 | # Average: O(nlog(n)) time | 0(1) space 19 | # Worst: O(nlog(n)) time | 0(1) space 20 | def heapSort(array): 21 | buildMaxHeap(array) 22 | for endIdx in reversed(range(1, len(array))): 23 | swap(0, endIdx, array) 24 | siftDown(0, endIdx - 1, array) 25 | return array 26 | 27 | def buildMaxHeap(array): 28 | firstParentIdx = (len(array) - 2) // 2 29 | for currentIdx in reversed(range(firstParentIdx + 1)): 30 | siftDown(currentIdx, len(array) - 1, array) 31 | 32 | def siftDown(currentIdx, endIdx, heap): 33 | childoneIdx = currentIdx * 2 + 1 34 | while childoneIdx <= endIdx: 35 | childTwoIdx = currentIdx * 2 + 2 if currentIdx * 2 + 2 <= endIdx else -1 36 | if childTwoIdx > -1 and heap[childTwoIdx] > heap[childoneIdx]: 37 | idxToSwap = childTwoIdx 38 | else: 39 | idxToSwap = childoneIdx 40 | if heap[idxToSwap] > heap[currentIdx]: 41 | swap(currentIdx, idxToSwap, heap) 42 | currentIdx = idxToSwap 43 | childoneIdx = currentIdx * 2 + 1 44 | else: 45 | return 46 | 47 | 48 | def swap(i, j, array): 49 | array[i], array[j] = array[j], array[i] 50 | 51 | -------------------------------------------------------------------------------- /Height Balanced Binary Tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Height Balanced Binary Tree 3 | 4 | You're given the root node of a Binary Tree.Write a function that returns true 5 | if this Binary Tree is height balanced and false if it isn't. 6 | 7 | A Binary Tree is height balanced if for each node in the tree,the difference 8 | between the height of its left subtree and the height of its right subtree 9 | is at most 1. 10 | 11 | Each BinaryTree node has an integer value,a left child node, and a right child 12 | node.Children nodes can either be BinaryTree nodes themselves or None/ null. 13 | 14 | Sample Input 15 | tree = 1 16 | / \ 17 | 2 3 18 | / \ \ 19 | 4 5 6 20 | / \ 21 | 7 8 22 | 23 | Sample Output 24 | true 25 | 26 | """ 27 | 28 | # SOLUTION 1 29 | 30 | class BinaryTree: 31 | def __init__(self, value, left=None, right=None): 32 | self.value = value 33 | self.left = left 34 | self.right = right 35 | 36 | 37 | class TreeInfo: 38 | def __init__(self, isBalanced, height): 39 | self.isBalanced = isBalanced 40 | self.height = height 41 | 42 | 43 | # O(n) time | O(h) space where n is the number of nodes in the tree 44 | # h is the height of the binary of Tree 45 | def heightBalancedBinaryTree(tree): 46 | treeInfo = getTreeInfo(tree) 47 | return treeInfo.isBalanced 48 | 49 | 50 | def getTreeInfo(node): 51 | if node is None: 52 | return TreeInfo(True, -1) 53 | leftSubtreeInfo = getTreeInfo(node.left) 54 | rightSubtreeInfo = getTreeInfo(node.right) 55 | 56 | isBalanced = ( 57 | leftSubtreeInfo.isBalanced 58 | and rightSubtreeInfo.isBalanced 59 | and abs(leftSubtreeInfo.height - rightSubtreeInfo.height) <= 1 60 | ) 61 | height = max(leftSubtreeInfo.height, rightSubtreeInfo.height) + 1 62 | return TreeInfo(isBalanced, height) 63 | -------------------------------------------------------------------------------- /Index Equals Value.py: -------------------------------------------------------------------------------- 1 | """ 2 | Index Equals Value 0 ☆ 3 | Write a function that takes in a sorted array of distinct integers and returns the first index in the array that is equal to the value at that 4 | index. In other words, your function should return the minimum index where index == array[index]. 5 | If there is no such index, your function should return -1. 6 | Sample Input 7 | array = [-5, -3, 2, 3, 4, 5, 9] 8 | Sample Output 9 | 3 // 3 == array[3] 10 | 11 | """ 12 | 13 | # SOLUTION 1 14 | 15 | # O(log(n)) time | 0(log(n)) space where n is the length of the input array 16 | def indexEqualsValue(array): 17 | return indexEqualsValueHelper (array, 0, len(array) - 1) 18 | 19 | def indexEqualsValueHelper(array, leftIndex, rightIndex): 20 | if leftIndex > rightIndex: 21 | return -1 22 | 23 | middleIndex = leftIndex + (rightIndex - leftIndex) // 2 24 | middleValue = array[middleIndex] 25 | if middleValue < middleIndex: 26 | return indexEqualsValueHelper(array, middleIndex + 1, rightIndex) 27 | elif middleValue == middleIndex and middleIndex == 0: 28 | return middleIndex 29 | elif middleValue == middleIndex and array[middleIndex - 1] < middleIndex - 1: 30 | return middleIndex 31 | else: 32 | return indexEqualsValueHelper(array, leftIndex, middleIndex - 1) 33 | 34 | 35 | # SOLUTION 2 36 | 37 | # O(log(n)) time | 0(1) space - where n is the length of the input array 38 | def indexEqualsValue(array): 39 | leftIndex = 0 40 | rightIndex = len(array) - 1 41 | while leftIndex <= rightIndex: 42 | middleIndex = leftIndex + (rightIndex - leftIndex) // 2 43 | middleValue = array[middleIndex] 44 | 45 | if middleValue < middleIndex: 46 | leftIndex = middleIndex + 1 47 | elif middleValue == middleIndex and middleIndex == 0: 48 | return middleIndex 49 | elif middleValue == middleIndex and array[middleIndex - 1] < middleIndex - 1: 50 | return middleIndex 51 | else: 52 | rightIndex = middleIndex - 1 53 | 54 | return -1 55 | 56 | -------------------------------------------------------------------------------- /Insertion Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Insertion Sort 3 | 4 | Write a function that takes in an array of integers and returns a sorted version of that 5 | array.Use the Insertion Sort algorithm to sort the array. 6 | 7 | Sample Input 8 | array = [8, 5, 2, 9, 5, 6, 3] 9 | 10 | Sample Output 11 | [2, 3, 5, 5, 6, 8, 9] 12 | """ 13 | 14 | # SOLUTION 1 15 | 16 | # Running time O(n^2) | space 0(1) 17 | def insertionSort(array): 18 | for i in range(1, len(array)): 19 | j = i 20 | while j > 0 and array[j] < array[j - 1]: 21 | array[j], array[j - 1] = array[j - 1], array[j] 22 | j -= 1 23 | 24 | return array 25 | 26 | 27 | -------------------------------------------------------------------------------- /Interweaving Strings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interweaving Strings ☆ 3 | Write a function that takes in three strings and returns a boolean representing whether the third string can be formed by interweaving 4 | the first two strings. 5 | To interweave strings means to merge them by alternating their letters without any specific pattern. For instance, the strings "abc" 6 | and "123" can be interwoven as "a1b2c3" , as "abc123" , and as "ab1c23" (this list is nonexhaustive). 7 | Letters within a string must maintain their relative ordering in the interwoven string. 8 | 9 | Sample Input 10 | one = "algoexpert" 11 | two = "your-dream-job" 12 | three = "your-algodream-expertjob" 13 | 14 | Sample Output 15 | true 16 | 17 | """ 18 | 19 | # SOLUTION 1 20 | 21 | # 0(2^(n + m)) time | O(n + m) space where n is the length 22 | # of the first string and m is the length of the second string 23 | def interweavingStrings(one, two, three): 24 | if len(three) != len(one) + len(two): 25 | return False 26 | return areInterwoven(one, two, three, 0, 0) 27 | 28 | 29 | def areInterwoven(one, two, three, i, j): 30 | k = i + j 31 | if k == len(three): 32 | return True 33 | 34 | if i < len(one) and one[i] == three[k]: 35 | if areInterwoven(one, two, three, i + 1, j): 36 | return True 37 | 38 | if j < len(two) and two[j] == three[k]: 39 | return areInterwoven(one, two, three, i, j + 1) 40 | 41 | return False 42 | 43 | 44 | # SOLUTION 2 45 | 46 | # O(nm) time | O(nm) space where n is the length of the 47 | # first string and m is the length of the second string 48 | def interweavingStrings(one, two, three): 49 | if len(three) != len(one) + len(two): 50 | return False 51 | 52 | cache = [[None for j in range(len(two) + 1)] for i in range(len(one) + 1)] 53 | return areInterwoven(one, two, three, 0, 0, cache) 54 | 55 | 56 | def areInterwoven(one, two, three, i, j, cache): 57 | if cache[i][j] is not None: 58 | return cache[i][j] 59 | 60 | k = i + j 61 | if k == len(three): 62 | return True 63 | 64 | if i < len(one) and one[i] == three[k]: 65 | cache[i][j] = areInterwoven(one, two, three, i + 1, j, cache) 66 | if cache[i][j]: 67 | return True 68 | if j < len(two) and two[j] == three[k]: 69 | cache[i][j] = areInterwoven(one, two, three, i, j + 1, cache) 70 | return cache[i][j] 71 | 72 | cache[i][j] = False 73 | return False 74 | -------------------------------------------------------------------------------- /Invert Binary Tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Invert Binary Tree 3 | 4 | Write a function that takes in a Binary Tree and inverts it.In other words,the function 5 | should swap every left node in the tree for its corresponding right node. 6 | 7 | Each BinaryTree node has an integer value, a left child node, and a right child node. 8 | Children nodes can either be BinaryTree nodes themselves or None/null. 9 | 10 | Sample Input 11 | tree = 1 12 | / \ 13 | 2 3 14 | / \ / \ 15 | 4 5 6 7 16 | / \ 17 | 8 9 18 | 19 | Sample Output 20 | 21 | tree = 1 22 | / \ 23 | 3 2 24 | / \ / \ 25 | 7 6 5 4 26 | / \ 27 | 9 8 28 | """ 29 | 30 | # SOLUTION 1 31 | 32 | # Recursive 33 | # O(n) time | O(d) space where d is longest depth of a branch 34 | def invertBinaryTree(tree): 35 | if tree is None: 36 | return 37 | if tree: 38 | tree.left, tree.right = tree.right, tree.left 39 | invertBinaryTree(tree.left) 40 | invertBinaryTree(tree.right) 41 | 42 | 43 | # This is the class of the input binary tree. 44 | class BinaryTree: 45 | def __init__(self, value): 46 | self.value = value 47 | self.left = None 48 | self.right = None 49 | 50 | 51 | # SOLUTION 2 52 | 53 | # Iterative 54 | # O(n) time | O(n) 55 | def invertBinaryTree(tree): 56 | queue = [tree] 57 | while len(queue): 58 | current = queue.pop(0) 59 | if current is None: 60 | continue 61 | current.left, current.right = current.right, current.left 62 | queue.append(current.left) 63 | queue.append(current.right) 64 | 65 | 66 | # This is the class of the input binary tree. 67 | class BinaryTree: 68 | def __init__(self, value): 69 | self.value = value 70 | self.left = None 71 | self.right = None 72 | -------------------------------------------------------------------------------- /Iterative In-order Traversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Iterative In-order Traversal ☆ 3 | Write a function that takes in a Binary Tree (where nodes have an additional pointer to their parent node) and traverses 4 | it iteratively using the in-order tree-traversal technique; the traversal should specifically not use recursion. As the 5 | tree is being traversed, a callback function passed in as anargument to the main function should be called on each node 6 | (i.e., callback(currentNode) ). 7 | Each BinaryTree node has an integer value , a parent node, a left child node, and a right child node. Children nodes 8 | can either be BinaryTree nodes themselves or None / null . 9 | 10 | Sample Input 11 | tree = 1 12 | / \ 13 | 2 3 14 | / / \ 15 | 4 6 7 16 | \ 17 | 9 18 | callback = someCallback 19 | Sample Output 20 | // The input callback will have been called in the following order: 21 | callback(4) 22 | callback(9) 23 | callback(2) 24 | callback(1) 25 | callback(6) 26 | callback(3) 27 | callback(7) 28 | 29 | """ 30 | # SOLUTION 1 31 | 32 | # O(n) time | 0(1) space 33 | def iterativeInOrderTraversal(tree, callback): 34 | previousNode = None 35 | currentNode = tree 36 | while currentNode is not None: 37 | if previousNode is None or previousNode == currentNode.parent: 38 | if currentNode.left is not None: 39 | nextNode = currentNode.left 40 | else: 41 | callback(currentNode) 42 | nextNode = currentNode.right if currentNode.right is not None else currentNode.parent 43 | elif previousNode == currentNode. left: 44 | callback(currentNode) 45 | nextNode = currentNode.right if currentNode.right is not None else currentNode.parent 46 | else: 47 | nextNode = currentNode.parent 48 | previousNode = currentNode 49 | currentNode = nextNode 50 | -------------------------------------------------------------------------------- /Kadane's Algorithm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Kadane's Algorithm 3 | 4 | Write a function that takes in a non-empty array of integers and returns the 5 | maximum sum that can be obtained by summing up all of the integers in a non-empty 6 | subarray of the input array.A subarray must only contain adjacent numbers (numbers 7 | next to each other in the input array). 8 | 9 | Sample Input 10 | array = [3, 5,-9, 1, 3, -2, 3, 4, 7, 2, -9, 6, 3, 1, -5, 4] 11 | 12 | Sample Output 13 | 19 // [1, 3, -2, 3, 4, 7, 2, -9, 6, 3, 1] 14 | 15 | 16 | """ 17 | 18 | # SOLUTION 1 19 | 20 | # O(n) time | O(1) space where n is the length of the input array. 21 | def kadanesAlgorithm(array): 22 | maxEndingHere = array[0] 23 | maxSoFar = array[0] 24 | for i in range(1, len(array)): 25 | num = array[i] 26 | maxEndingHere = max(num, maxEndingHere + num) 27 | maxSoFar = max(maxSoFar, maxEndingHere) 28 | return maxSoFar 29 | -------------------------------------------------------------------------------- /Knapsack Problem.py: -------------------------------------------------------------------------------- 1 | """ 2 | Knapsack Problem 3 | 4 | You're given an array of arrays where each subarray holds two integer values and represents an item; the 5 | first integer is the item's value, and the second integer is the item's weight. You're also given an integer 6 | representing the maximum capacity of a knapsack that you have. 7 | Your goal is to fit items in your knapsack without having the sum of their weights exceed the knapsack's 8 | capacity, all the while maximizing their combined value. Note that you only have one of each item at your 9 | disposal. 10 | Write a function that returns the maximized combined value of the items that you should pick as well as an 11 | array of the indices of each item picked. 12 | If there are multiple combinations of items that maximize the total value in the knapsack, your function can 13 | return any of them. 14 | 15 | Sample Input 16 | items = [[1, 2], [4, 3], [5, 6], [6, 7]] 17 | capacity = 10 18 | Sample Output 19 | [10, [1, 3]] // items [4, 3] and [6, 7] 20 | 21 | """ 22 | # SOLUTION 1 23 | 24 | # O(nc) time | O(nc) space 25 | def knapsackProblem(items, capacity): 26 | knapsackValues = [[0 for x in range(0, capacity + 1)] for y in range(0, len(items) + 1)] 27 | for i in range(1, len(items) + 1): 28 | currentWeight = items[i - 1][1] 29 | currentValue = items[i - 1][0] 30 | for c in range(0, capacity + 1): 31 | if currentWeight > c: 32 | knapsackValues[i][c] = knapsackValues[i - 1][c] 33 | else: 34 | knapsackValues[i][c] = max(knapsackValues[i - 1][c], knapsackValues[i - 1][c - currentWeight] + currentValue) 35 | return [knapsackValues[-1][-1], getknapsackItems(knapsackValues, items)] 36 | 37 | def getknapsackItems(knapsackValues, items): 38 | sequence = [] 39 | i = len(knapsackValues) - 1 40 | c = len(knapsackValues[0]) - 1 41 | while i > 0: 42 | if knapsackValues[i][c] == knapsackValues[i - 1][c]: 43 | i -= 1 44 | else: 45 | sequence.append(i - 1) 46 | c -= items[i - 1][1] 47 | i -= 1 48 | if c == 0: 49 | break 50 | return list(reversed(sequence)) 51 | 52 | 53 | -------------------------------------------------------------------------------- /Knuth-Morris-Pratt Algorithm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Knuth—Morris—Pratt Algorithm 3 | Write a function that takes in two strings and checks if the first string contains the second one using the 4 | Knuth—Morris—Pratt algorithm. The function should return a boolean. 5 | If you're unfamiliar with the Knuth—Morris—Pratt Algorithm, we recommend watching the Conceptual Overview section 6 | of this question's video explanation before starting to code. 7 | 8 | Sample Input 9 | string = "aefoaefcdaefcdaed" 10 | substring = "aefcdaed" 11 | 12 | Sample Output 13 | true 14 | 15 | """ 16 | 17 | # SOLUTION 1 18 | 19 | # O(n + m) time | 0(m) space 20 | def knuthMorrisPrattAlgorithm(string, substring): 21 | pattern = buildPattern(substring) 22 | return doesMatch(string, substring, pattern) 23 | 24 | def buildPattern(substring): 25 | pattern = [-1 for i in substring] 26 | j = 0 27 | i = 1 28 | while i < len(substring): 29 | if substring[i] == substring[j]: 30 | pattern[i] =j 31 | i += 1 32 | j += 1 33 | elif j>0: 34 | j = pattern[j - 1] + 1 35 | else: 36 | i += 1 37 | return pattern 38 | 39 | def doesMatch(string, substring, pattern): 40 | i = 0 41 | j = 0 42 | while i + len(substring) - j <= len(string): 43 | if string[i] == substring[j]: 44 | if j == len(substring) - 1: 45 | return True 46 | i += 1 47 | j += 1 48 | elif j > 0: 49 | j = pattern[j - 1] + 1 50 | 51 | else: 52 | i += 1 53 | return False 54 | -------------------------------------------------------------------------------- /Largest Range.py: -------------------------------------------------------------------------------- 1 | """ 2 | Largest Range 3 | Write a function that takes in an array of integers and returns an array of length 2 representing the largest range of 4 | integers contained in that array.The first number in the output array should be the first number in the range, while the 5 | second number should be the last number in the range. A range of numbers is defined as a set of numbers that come right 6 | after each other in the set of real integers. For instance, the output array [2, 6] represents the range {2, 3, 4, 5, 6} 7 | ,which is a range of length 5. Note that numbers don't need to be sorted or adjacent in the input array in order to form 8 | a range. 9 | 10 | You can assume that there will only be one largest range. 11 | 12 | Sample Input 13 | 14 | array = [1, 11, 3, 0, 15, 5, 2, 4, 10, 7, 12, 6] 15 | 16 | Sample Output 17 | [0, 7] 18 | 19 | """ 20 | 21 | # SOLUTION 1 22 | 23 | # O(n) time | O(n) space 24 | def largestRange(array): 25 | bestRange = [] 26 | longestLength = 0 27 | nums = {} 28 | for num in array: 29 | nums[num] = True 30 | for num in array: 31 | if not nums[num]: 32 | continue 33 | nums[num] = False 34 | currentLength = 1 35 | left = num - 1 36 | right = num + 1 37 | while left in nums: 38 | nums[left] = False 39 | currentLength += 1 40 | left -= 1 41 | while right in nums: 42 | nums[right] = False 43 | currentLength += 1 44 | right += 1 45 | if currentLength > longestLength: 46 | longestLength = currentLength 47 | bestRange = [left + 1, right - 1] 48 | return bestRange 49 | -------------------------------------------------------------------------------- /Largest Rectangle Under Skyline.py: -------------------------------------------------------------------------------- 1 | """ 2 | Largest Rectangle Under Skyline o ☆ 3 | Write a function that takes in an array of positive integers representing the heights of adjacent buildings and returns the 4 | area of the largest rectangle that can be created by any number of adjacent buildings, including just one building. Note 5 | that all buildings have the same width of 1 unit. 6 | For example, given buildings = [2, 1, 2] , the area of the largest rectangle that can be created is 3 , using all 7 | three buildings. Since the minimum height of the three buildings is 1 , you can create a rectangle that has a height of 8 | 1 and a width of 3 (the number of buildings). You could also create rectangles of area 2 by using only the first 9 | building or the last building, but these clearly wouldn't be the largest rectangles. Similarly, you could create rectangles of 10 | area 2 by using the first and second building or the second and third building. 11 | To clarify, the width of a created rectangle is the number of buildings used to create the rectangle, and its height is the 12 | height of the smallest building used to create it. 13 | Note that if no rectangles can be created, your function should return o. 14 | 15 | Sample Input 16 | buildings = [1, 3, 3, 2, 4, 1, 5, 3, 2] 17 | Sample Output 18 | 9 19 | // Below is a visual representation of the sample input. 20 | // _ 21 | // _ | | 22 | // _ _ | | | |_ 23 | // | | |_| | | | |_ 24 | // _| | | | |_| | | | 25 | // |_|_|_|_|_|_|_|_|_| 26 | 27 | """ 28 | 29 | # SOLUTION 1 30 | 31 | # O(n^2) time | 0(1) space - where n is the number of buildings 32 | def largestRectangleUnderSkyline(buildings): 33 | maxArea = 0 34 | for pillarIdx in range(len(buildings)): 35 | currentHeight = buildings[pillarIdx] 36 | 37 | furthestLeft = pillarIdx 38 | while furthestLeft > 0 and buildings[furthestLeft - 1] >= currentHeight: 39 | furthestLeft -= 1 40 | 41 | furthestRight = pillarIdx 42 | while furthestRight < len(buildings) - 1 and buildings[furthestRight + 1] >= currentHeight: 43 | furthestRight += 1 44 | areaWithCurrentBuilding = (furthestRight - furthestLeft + 1) * currentHeight 45 | maxArea = max(areaWithCurrentBuilding, maxArea) 46 | return maxArea 47 | 48 | 49 | # SOLUTION 2 50 | 51 | # O(n) time | O(n) space - where n is the number of buildings 52 | def largestRectangleUnderSkyline(buildings): 53 | pillarIndices = [] 54 | maxArea = 0 55 | 56 | for idx, height in enumerate(buildings + [0]): 57 | while len(pillarIndices) != 0 and buildings[pillarIndices[len(pillarIndices) - 1]] >= height: 58 | pillarHeight = buildings[pillarIndices.pop()] 59 | width = idx if len(pillarIndices) == 0 else idx - pillarIndices[len(pillarIndices) - 1] - 1 60 | maxArea = max(width * pillarHeight, maxArea) 61 | 62 | pillarIndices.append(idx) 63 | 64 | return maxArea 65 | -------------------------------------------------------------------------------- /Levenshtein Distance.py: -------------------------------------------------------------------------------- 1 | """ 2 | Levenshtein Distance 3 | 4 | Write a function that takes in two strings and returns the minimum number of 5 | edit operations that need to be performed on the first string to obtain the 6 | second string. 7 | 8 | There are three edit operations: Insertion of a character, deletion of a 9 | character, and substitution of a character for another. 10 | 11 | Sample Input 12 | str1 = "abc" 13 | str2 = "yabd" 14 | 15 | Sample Output 16 | 2 // insert "y"; substitute "c" for "d" 17 | 18 | 19 | """ 20 | # SOLUTION 1 21 | 22 | # O(n*m) time | O(n) space where n and m are the strings lengths. 23 | def levenshteinDistance(str1, str2): 24 | if str1 == '' or str2 == '': 25 | return len(str1) or len(str2) 26 | 27 | dp = [[float('inf') for _ in range(len(str2) + 1)] for _ in range(len(str1) + 1)] 28 | dp[0][0] = 0 29 | for i in range(1, len(dp)): 30 | dp[i][0] = i 31 | for j in range(1, len(dp[0])): 32 | dp[0][j] = j 33 | 34 | for i in range(1, len(dp)): 35 | for j in range(1, len(dp[0])): 36 | if str1[i - 1] == str2[j - 1]: 37 | dp[i][j] = dp[i - 1][j - 1] 38 | else: 39 | dp[i][j] = min(dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j]) + 1 40 | 41 | return dp[-1][-1] 42 | 43 | 44 | # SOLUTION 2 45 | 46 | # O(n*m) time | O(n) space where n and m are the strings lengths. 47 | def levenshteinDistance(str1, str2): 48 | dp = [[x for x in range(len(str1) + 1)] for _ in range(len(str2) + 1)] 49 | for i in range(1, len(str2) + 1): 50 | dp[i][0] = dp[i - 1][0] + 1 51 | for i in range(1, len(str2) + 1): 52 | for j in range(1, len(str1) + 1): 53 | if str2[i - 1] == str1[j - 1]: 54 | dp[i][j] = dp[i - 1][j - 1] 55 | else: 56 | dp[i][j] = 1 + min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) 57 | 58 | return dp[-1][-1] 59 | 60 | 61 | -------------------------------------------------------------------------------- /Line Through Points.py: -------------------------------------------------------------------------------- 1 | """ 2 | Line Through Points o ☆ 3 | You're given an array of points plotted on a 2D graph (the xy-plane). Write a function that returns the 4 | maximum number of points that a single line (or potentially multiple lines) on the graph passes through. 5 | The input array will contain points represented by an array of two integers [x, y] . The input array will 6 | never contain duplicate points and will always contain at least one point. 7 | Sample Input 8 | points = [ 9 | [1, 1], 10 | [2, 2], 11 | (3, 3), 12 | [0, 4], 13 | [-2, 6], 14 | [4, 0], 15 | [2, 1], 16 | ] 17 | Sample Output 18 | 4 // A line passes through points: [-2, -6], [0, 4], [2, 2], [4, 0]. 19 | 20 | """ 21 | 22 | # SOLUTION 1 23 | 24 | # O(n^2) time | O(n) space - where n is the number of points 25 | def lineThroughPoints(points): 26 | maxNumberOfPointsOnLine = 1 27 | for idx1, p1 in enumerate(points): 28 | slopes = {} 29 | for idx2 in range(idx1 + 1, len(points)): 30 | p2 = points[idx2] 31 | rise, run = getSlopeOfLineBetweenPoints(p1, p2) 32 | slopekey = createHashableKeyForRational(rise, run) 33 | if slopekey not in slopes: 34 | slopes[slopekey] = 1 35 | slopes[slopekey] += 1 36 | maxNumberOfPointsOnLine = max(maxNumberOfPointsOnLine, max(slopes.values(), default=0)) 37 | return maxNumberOfPointsOnLine 38 | 39 | def getSlopeOfLineBetweenPoints(p1, p2): 40 | p1x, p1y = p1 41 | p2x, p2y = p2 42 | slope = [1, 0] # slope of a vertical line 43 | 44 | if p1x != p2x: # If line is not vertical 45 | xDiff = p1x - p2x 46 | yDiff = p1y - p2y 47 | gcd = getGreatestCommonDivisor(abs(xDiff), abs(yDiff)) 48 | xDiff = xDiff // gcd 49 | yDiff = yDiff // gcd 50 | if xDiff < 0: 51 | xDiff *= -1 52 | yDiff *= -1 53 | 54 | slope = [yDiff, xDiff] 55 | return slope 56 | 57 | def createHashableKeyForRational(numerator, denominator): 58 | return str(numerator) + ":" + str(denominator) 59 | 60 | def getGreatestCommonDivisor(num1, num2): 61 | a = num1 62 | b = num2 63 | while True: 64 | if a== 0: 65 | return b 66 | if b== 0: 67 | return a 68 | a,b = b, a % b -------------------------------------------------------------------------------- /Longest Increasing Subsequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Longest Increasing Subsequence 3 | Given a non-empty array of integers, write a function that returns the longest strictly-increasing subsequence in the 4 | array. 5 | A subsequence of an array is a set of numbers that aren't necessarily adjacent in the array but that are in the same 6 | order as they appear in the array. For instance, the numbers [1, 3, 4] form a subsequence of the array 7 | [1, 2, 3, 4] , and so do the numbers [2, 4] . Note that a single number in an array and the array itself are both 8 | valid subsequences of the array. 9 | You can assume that there will only be one longest increasing subsequence. 10 | 11 | Sample Input 12 | array = [5, 7, -24, 12, 10, 2, 3, 12, 5, 6, 35] 13 | 14 | Sample Output 15 | [-24, 2, 3, 5, 6, 35] 16 | 17 | """ 18 | 19 | # SOLUTION 1 20 | 21 | # O(n^2) time | O(n) space 22 | def longestIncreasingSubsequence(array): 23 | sequences = [None for x in array] 24 | lengths = [1 for x in array] 25 | maxlengthIdx = 0 26 | for i in range(len(array)): 27 | currentNum = array[i] 28 | for j in range(0, i): 29 | otherNum = array[j] 30 | if otherNum < currentNum and lengths[j] + 1 >= lengths[i]: 31 | lengths[i] = lengths[j] + 1 32 | sequences[i] = j 33 | if lengths[i] >= lengths[maxlengthIdx]: 34 | maxlengthIdx = i 35 | return buildSequence(array, sequences, maxlengthIdx) 36 | 37 | def buildSequence(array, sequences, currentIdx): 38 | sequence = [] 39 | while currentIdx is not None: 40 | sequence.append(array[currentIdx]) 41 | currentIdx = sequences[currentIdx] 42 | return list(reversed( sequence)) 43 | 44 | 45 | # SOLUTION 2 46 | 47 | # O(nlogn) time | O(n) space 48 | def longestIncreasingSubsequence(array): 49 | sequences = [None for x in array] 50 | indices = [None for x in range(len(array) + 1)] 51 | length = 0 52 | for i, num in enumerate(array): 53 | newLength = binarySearch(1, length, indices, array, num) 54 | sequences[i] = indices[newLength - 1] 55 | indices [newLength] = i 56 | length = max(length, newLength) 57 | return buildSequence(array, sequences, indices[length]) 58 | 59 | def binarySearch(startIdx, endIdx, indices, array, num): 60 | if startIdx > endIdx: 61 | return startIdx 62 | middleIdx = (startIdx + endIdx) // 2 63 | if array[indices[middleIdx]] < num: 64 | startIdx = middleIdx + 1 65 | else: 66 | endIdx = middleIdx - 1 67 | return binarySearch(startIdx, endIdx, indices, array, num) 68 | def buildSequence(array, sequences, currentIdx): 69 | sequence = [] 70 | while currentIdx is not None: 71 | sequence.append(array[currentIdx]) 72 | currentIdx = sequences[currentIdx] 73 | return list(reversed( sequence)) 74 | -------------------------------------------------------------------------------- /Longest Palindromic Substring.py: -------------------------------------------------------------------------------- 1 | """ 2 | Longest Palindromic Substring 3 | 4 | Write a function that,given a string returns its longest palindromic substring. 5 | 6 | A palindrome is defined as a string thats written the same forward and backward.Note 7 | that single-character strings are palindromes. 8 | 9 | You can assume that there will only be one longest palindromic substring. 10 | 11 | Sample Input 12 | string = "abaxyzzyxf" 13 | 14 | Sample Output 15 | "xyzzyz" 16 | """ 17 | 18 | # SOLUTION 1 19 | 20 | # O(n^3) time | O(n) space 21 | def longestPalindromicSubstring(string): 22 | longest = '' 23 | for i in range(len(string)): 24 | for j in range(i, len(string)): 25 | substring = string[i: j + 1] 26 | if len(substring) > len(longest) and isPalindrome(substring): 27 | longest = substring 28 | return longest 29 | 30 | def isPalindrome(string): 31 | left = 0 32 | right = len(string) - 1 33 | 34 | while left < right: 35 | if string[left] != string[right]: 36 | return False 37 | left += 1 38 | right -= 1 39 | return True 40 | 41 | -------------------------------------------------------------------------------- /Longest Peak.py: -------------------------------------------------------------------------------- 1 | """ 2 | Longest Peak 3 | 4 | Write a function that takes in array of integers and returns the length of the longest 5 | peak in the array. 6 | 7 | A peak is defined as adjacent integers in the array that are strictly increasing until 8 | they reach a tip (the highest value in the peak). at which point they become strictly 9 | decreasing.At least three integers are required to form a peak. 10 | 11 | For example,the integers 1, 4, 10, 2 form a peak, but the integers 4, 0, 10 don't and 12 | neither do the integers 1, 2, 2, 0. Similarly,the integers 1, 2, 3 don't form a peak 13 | because there aren't any strictly decreasing integers after the 3. 14 | 15 | Sample Input 16 | array = [1, 2, 3, 3, 4, 0, 10, 6, 5, -1, -3, 2, 3] 17 | 18 | Sample Output 19 | 6 // 0, 10, 6, 5, -1, -3 20 | 21 | """ 22 | 23 | # SOLUTION 1 24 | 25 | # O(n) time | O(1) space where n is the length of the input array: 26 | def longestPeak(array): 27 | longestPeakLength = 0 28 | i = 1 29 | while i < len(array) - 1: 30 | isPeak = array[i - 1] < array[i] and array[i] > array[i + 1] 31 | if not isPeak: 32 | i += 1 33 | continue 34 | leftIdx = i - 2 35 | while leftIdx >= 0 and array[leftIdx] < array[leftIdx + 1]: 36 | leftIdx -= 1 37 | rightIdx = i + 2 38 | while rightIdx < len(array) and array[rightIdx] < array[rightIdx - 1]: 39 | rightIdx += 1 40 | 41 | currentPeakLength = rightIdx - leftIdx - 1 42 | longestPeakLength = max(longestPeakLength, currentPeakLength) 43 | i = rightIdx 44 | return longestPeakLength 45 | 46 | 47 | -------------------------------------------------------------------------------- /Longest Substring Without Duplication.py: -------------------------------------------------------------------------------- 1 | """ 2 | Longest Substring Without Duplication * 3 | Write a function that takes in a string and returns its longest substring without duplicate characters. 4 | You can assume that there will only be one longest substring without duplication. 5 | 6 | Sample Input 7 | string = "clementisacap" 8 | Sample Output 9 | "mentisac" 10 | 11 | """ 12 | # SOLUTION 1 13 | 14 | # O(n) time | 0(min(n, a)) space 15 | def longestSubstringWithoutDuplication(string): 16 | lastSeen = {} 17 | longest = [0, 1] 18 | startIdx = 0 19 | for i, char in enumerate(string): 20 | if char in lastSeen: 21 | startIdx = max(startIdx, lastSeen[char] + 1) 22 | if longest[1] - longest[0] < i + 1 - startIdx: 23 | longest = [startIdx, i + 1] 24 | lastSeen[char] = i 25 | return string[longest[0] : longest[1]] 26 | 27 | -------------------------------------------------------------------------------- /Lowest Common Manager.py: -------------------------------------------------------------------------------- 1 | """ 2 | Lowest Common Manager ☆ 3 | You're given three inputs, all of which are instances of an OrgChart class that have a directReports property 4 | pointing to their direct reports. The first input is the top manager in an organizational chart (i.e., the only instance that 5 | isn't anybody else's direct report), and the other two inputs are reports in the organizational chart. The two reports are 6 | guaranteed to be distinct. 7 | Write a function that returns the lowest common manager to the two reports. 8 | 9 | Sample Input 10 | // From the organizational chart below. 11 | topManager = Node A 12 | reportone = Node E 13 | reportTwo = Node I 14 | tree = A 15 | / \ 16 | B C 17 | / \ / \ 18 | D E F G 19 | / \ 20 | H I 21 | 22 | Sample Output 23 | Node B 24 | 25 | """ 26 | 27 | # SOLUTION 1 28 | 29 | # O(n) time | 0(d) space where n is the number of people 30 | # in the org and d is the depth (height) of the org chart 31 | def getLowestCommonManager(topManager, reportOne, reportTwo): 32 | return getOrgInfo(topManager, reportOne, reportTwo). lowestCommonManager 33 | 34 | def getOrgInfo(manager, reportOne, reportTwo): 35 | numImportantReports = 0 36 | for directReport in manager.directReports: 37 | orgInfo = getOrgInfo(directReport, reportOne, reportTwo) 38 | if orgInfo.lowestCommonManager is not None: 39 | return orgInfo 40 | numImportantReports += orgInfo.numImportantReports 41 | if manager == reportOne or manager == reportTwo: 42 | numImportantReports += 1 43 | lowestCommonManager = manager if numImportantReports == 2 else None 44 | return OrgInfo(lowestCommonManager, numImportantReports) 45 | 46 | class OrgInfo: 47 | def __init__(self, lowestCommonManager, numImportantReports): 48 | self.lowestCommonManager = lowestCommonManager 49 | self.numImportantReports = numImportantReports 50 | 51 | # This is the input class. 52 | class OrgChart: 53 | def __init__(self, name): 54 | self.name = name 55 | self.directReports = [] 56 | -------------------------------------------------------------------------------- /Max Path Sum In Binary Tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Max Path Sum In Binary Tree ♡ 3 | 4 | Write a function that takes in a Binary Tree and returns its max path sum. 5 | A path is a collection of connected nodes in a tree, where no node is connected to more than two other 6 | nodes; a path sum is the sum of the values of the nodes in a particular path. 7 | Each BinaryTree node has an integer value , a left child node, and a right child node. Children 8 | nodes can either be BinaryTree nodes themselves or None / null . 9 | 10 | Sample Input 11 | tree = 1 12 | / \ 13 | 2 3 14 | / \ / \ 15 | 4 5 6 7 16 | 17 | Sample Output 18 | 18 // 5 + 2 + 1 + 3 + 7 19 | 20 | """ 21 | 22 | # SOLUTION 1 23 | 24 | # O(n) time | O(log(n)) space 25 | def maxPathSum(tree): 26 | _, maxSum = findMaxSum(tree) 27 | return maxSum 28 | 29 | 30 | def findMaxSum(tree): 31 | if tree is None: 32 | return 0, float("-inf") 33 | 34 | leftMaxSumAsBranch, leftMaxPathSum = findMaxSum(tree.left) 35 | rightMaxSumAsBranch, rightMaxPathSum = findMaxSum(tree.right) 36 | maxChildSumAsBranch = max(leftMaxSumAsBranch, rightMaxSumAsBranch) 37 | 38 | value = tree.value 39 | 40 | maxSumAsBranch = max(maxChildSumAsBranch + value, value) 41 | maxSumAsRootNode = max(leftMaxSumAsBranch + value + rightMaxSumAsBranch, maxSumAsBranch) 42 | maxPathSum = max(leftMaxPathSum, rightMaxPathSum, maxSumAsRootNode) 43 | 44 | return maxSumAsBranch, maxPathSum 45 | -------------------------------------------------------------------------------- /Max Profit With K Transactions.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Max Profit With K Transactions 4 | 5 | You're given an array of positive integers representing the prices of a single stock on various days (each index in the 6 | array represents a different day). You're also given an integer k, which represents the number of transactions you're 7 | allowed to make. One transaction consists of buying the stock on a given day and selling it on another, later day. 8 | Write a function that returns the maximum profit that you can make by buying and selling the stock, given k 9 | transactions. 10 | Note that you can only hold one share of the stock at a time; in other words, you can't buy more than one share of the 11 | stock on any given day, and you can't buy a share of the stock if you're still holding another share. Also, you don't need 12 | to use all k transactions that you're allowed. 13 | 14 | Sample Input 15 | prices = [5, 11, 3, 50, 60, 90] 16 | k = 2 17 | 18 | Sample Output 19 | 9 3 // Buy: 5, Sell: 11; Buy: 3, Sell: 90 20 | 21 | """ 22 | 23 | # SOLUTION 1 24 | 25 | # O(nk) time | O(nk) space 26 | def maxProfitWithKTransactions(prices, k): 27 | if not len(prices): 28 | return 0 29 | profits = [[0 for d in prices] for t in range(k + 1)] 30 | for t in range(1, k + 1): 31 | maxThusFar = float("-inf") 32 | for d in range(1, len(prices)): 33 | maxThusFar = max(maxThusFar, profits[t - 1][d - 1] - prices[d - 1]) 34 | profits[t][d] = max(profits[t][d - 1], maxThusFar + prices[d]) 35 | return profits[-1][-1] 36 | 37 | 38 | # SOLUTION 2 39 | 40 | # O(nk) time | O(n) space 41 | def maxProfitWithKTransactions(prices, k): 42 | if not len(prices): 43 | return 0 44 | evenProfits = [0 for d in prices] 45 | oddProfits = [0 for d in prices] 46 | for t in range(1, k + 1): 47 | maxThusFar = float("-inf") 48 | if t % 2 == 1: 49 | currentProfits = oddProfits 50 | previousProfits = evenProfits 51 | else: 52 | currentProfits = evenProfits 53 | previousProfits = oddProfits 54 | 55 | for d in range(1, len(prices)): 56 | maxThusFar = max(maxThusFar, previousProfits[d - 1] - prices[d - 1]) 57 | currentProfits[d] = max(currentProfits[d - 1], maxThusFar + prices[d]) 58 | return evenProfits[-1] if k % 2 == 0 else oddProfits[-1] 59 | 60 | 61 | -------------------------------------------------------------------------------- /Max Subset Sum No Adjacent.py: -------------------------------------------------------------------------------- 1 | """ 2 | Max Subset Sum No Adjacent 3 | 4 | Write a function that takes in array of positive integers and returns the maximum 5 | sum of non-adjacent elements in the array. 6 | 7 | If the input array is empty ,the function should return 0. 8 | 9 | Sample Input 10 | array = [75, 105, 120, 75, 90, 135] 11 | 12 | 13 | Sample Output 14 | 330 // 75 + 120 + 135 15 | """ 16 | 17 | # SOLUTION 1 18 | 19 | # O(n) time | O(n) space 20 | def maxSubsetSumNoAdjacent(array): 21 | if not len(array): 22 | return 0 23 | elif len(array) == 1: 24 | return array[0] 25 | maxSums = array[:] 26 | maxSums[1] = max(array[0], array[1]) 27 | for i in range(2, len(array)): 28 | maxSums[i] = max(maxSums[i-1], maxSums[i-2] + array[i]) 29 | return maxSums[-1] 30 | 31 | 32 | # SOLUTION 2 33 | 34 | # O(n) time | O(1) space 35 | def maxSubsetSumNoAdjacent(array): 36 | if not len(array): 37 | return 0 38 | elif len(array) == 1: 39 | return array[0] 40 | second = array[0] 41 | first = max(array[0], array[1]) 42 | for i in range(2, len(array)): 43 | current = max(first, second + array[i]) 44 | second = first 45 | first = current 46 | return first 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Max Sum Increasing Subsequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Max Sum Increasing Subsequence ☆ 3 | 4 | Write a function that takes in a non-empty array of integers and returns the greatest sum that can be generated from 5 | a strictly-increasing subsequence in the array as well as an array of the numbers in that subsequence. 6 | A subsequence of an array is a set of numbers that aren't necessarily adjacent in the array but that are in the same 7 | order as they appear in the array. For instance, the numbers [1, 3, 4] form a subsequence of the array [1, 2, 3, 4] , 8 | and so do the numbers [2, 4] . Note that a single number in an array and the array itself are both valid subsequences 9 | of the array. 10 | You can assume that there will only be one increasing subsequence with the greatest sum. 11 | 12 | Sample Input 13 | array = [10, 70, 20, 30, 50, 11, 30] 14 | 15 | Sample Output 16 | [110, [10, 20, 30, 50]] // The subsequence [10, 20, 30, 50] is strictly increasing and yields the greatest sum: 110. 17 | 18 | """ 19 | 20 | 21 | # SOLUTION 1 22 | 23 | # O(n^2) time | O(n) space 24 | def maxSumIncreasingSubsequence(array): 25 | sequences = [None for x in array] 26 | sums = [num for num in array] 27 | maxSumIdx = 0 28 | for i in range(len(array)): 29 | currentNum = array[i] 30 | for j in range(0, i): 31 | otherNum = array[j] 32 | if otherNum < currentNum and sums[j] + currentNum >= sums[i]: 33 | sums[i] = sums[j] + currentNum 34 | sequences[i] = j 35 | if sums[i] >= sums[maxSumIdx]: 36 | maxSumIdx = i 37 | return [sums [maxSumIdx], buildsequence (array, sequences, maxSumIdx)] 38 | 39 | def buildsequence(array, sequences, currentIdx): 40 | sequence = [] 41 | while currentIdx is not None: 42 | sequence.append(array[currentIdx]) 43 | currentIdx = sequences[currentIdx] 44 | return list(reversed(sequence)) 45 | -------------------------------------------------------------------------------- /Maximixie Expression.py: -------------------------------------------------------------------------------- 1 | """ 2 | Maximize Expression 3 | Write a function that takes in an array of integers and returns the largest possible value for the expression 4 | array[a] - array[b] + array[c] - array[d] , where a, b, c, and d are indices of the array 5 | and a < b 6 - (-3) + 2 - 7 = 4 14 | 15 | """ 16 | 17 | # SOLUTION 1 18 | 19 | # O(n^4) time | 0(1) space - where n is the length of the array 20 | def maximizeExpression(array): 21 | if len(array) < 4: 22 | return 0 23 | maximumvalueFound = float("-inf") 24 | for a in range(len(array)): 25 | avalue = array[a] 26 | for b in range(a + 1, len(array)): 27 | bvalue = array[b] 28 | for c in range(b + 1, len(array)): 29 | cValue = array[c] 30 | for d in range(c + 1, len(array)): 31 | dvalue = array[d] 32 | expressionValue = evaluateExpression(avalue, bvalue, cValue, dvalue) 33 | maximumvalueFound = max(expressionValue, maximumvalueFound) 34 | 35 | return maximumvalueFound 36 | 37 | 38 | def evaluateExpression(a, b, c, d): 39 | return a - b + c - d 40 | 41 | 42 | # SOLUTION 2 43 | 44 | # O(n) time | O(n) space where n is the length of the array 45 | def maximizeExpression(array): 46 | if len(array) < 4: 47 | return 0 48 | 49 | maxOfA = [array[0]] 50 | maxOfAMinusB = [float("-inf")] 51 | maxOfAMinusBPlusC = [float("-inf")] * 2 52 | maxOfAMinusBPlusCMinusD = [float("-inf")] * 3 53 | 54 | for idx in range(1, len(array)): 55 | currentMax = max(maxOfA[idx - 1], array[idx]) 56 | maxOfA.append(currentMax) 57 | 58 | for idx in range(1, len(array)): 59 | currentMax = max(maxOfAMinusB[idx - 1], maxOfA[idx - 1] - array[idx]) 60 | maxOfAMinusB.append(currentMax) 61 | for idx in range(2, len(array)): 62 | currentMax = max(maxOfAMinusBPlusC[idx - 1], maxOfAMinusB[idx - 1] + array[idx]) 63 | maxOfAMinusBPlusC.append(currentMax) 64 | 65 | for idx in range(3, len(array)): 66 | currentMax = max(maxOfAMinusBPlusCMinusD[idx - 1], maxOfAMinusBPlusC[idx - 1] - array[idx]) 67 | maxOfAMinusBPlusCMinusD.append(currentMax) 68 | 69 | return maxOfAMinusBPlusCMinusD[len(maxOfAMinusBPlusCMinusD) - 1] 70 | -------------------------------------------------------------------------------- /Maximum Sum Submatrix.py: -------------------------------------------------------------------------------- 1 | """ 2 | Maximum Sum Submatrix * 3 | You're given a two-dimensional array la matrix) of potentially unequal height and width that's filled with integers. 4 | You're also given a positive integer size.Write a function that returns the maximum sum that can be 5 | generated from a submatrix with dimensions size = size. 6 | For example, consider the following matrix 7 | [ 8 | [2, 4] 9 | [5, 6], 10 | [-3, 2] 11 | ] 12 | If size = 2, then the 2x2 submatrices to consider are: 13 | [2, 4] 14 | [5, 6] 15 | The sum of the elements in the first submatrix is 17 and the sum of the elements in the second submatrix is 18. 16 | In this example, your function should return 17 - 17 | Note: size will always be at least 1. and the dimensions of the input matrix will always be at least size * size. 18 | 19 | Sample Input 20 | matrix = 21 | [ 22 | [5, 3, -1, 5], 23 | [-7, 3, 7, 4], 24 | [12, 8, e, e], 25 | [1, -8, -8, 2], 26 | ] 27 | size = 2 28 | 29 | Sample Output 30 | 18 31 | // 32 | // [. ,. ,. ,. ], 33 | // [. , 3, 7,. ], 34 | // [., 8, 0 , .], 35 | // [., ., . , .] 36 | // ] 37 | 38 | """ 39 | # SOLUTION 1 40 | 41 | # O(w * h) time | (w h) space - where w is 42 | # the width of the matrix and h is the height 43 | def maximumSumSubmatrix(matrix, size): 44 | sums = createSumMatrix(matrix) 45 | maxSubMatrixSum = float("-inf") 46 | 47 | for row in range(size - 1, len(matrix)): 48 | for col in range(size - 1, len(matrix[row])): 49 | total = sums[row][col] 50 | touchesTopBorder = row - size < 0 51 | if not touchesTopBorder: 52 | total -= sums[row - size][col] 53 | 54 | touchesLeftBorder = col - size < 0 55 | if not touchesLeftBorder: 56 | total -= sums[row][col - size] 57 | touchesTopOrLeftBorder = touchesTopBorder or touchesLeftBorder 58 | if not touchesTopOrLeftBorder: 59 | total += sums[row - size][col - size] 60 | maxSubMatrixSum = max(maxSubMatrixSum, total) 61 | 62 | return maxSubMatrixSum 63 | def createSumMatrix(matrix): 64 | sums = [ [0 for _ in range(len(matrix[row]))] for row in range(len(matrix))] 65 | sums[0][0] = matrix[0][0] 66 | 67 | # Fill the first row. 68 | for idx in range(1, len(matrix[0])): 69 | sums[0][idx] = sums[0][idx - 1] + matrix[0][idx] 70 | 71 | # Fill the first column. 72 | for idx in range(1, len(matrix)): 73 | sums[idx][0] = sums[idx - 1][0] + matrix[idx][0] 74 | 75 | # Fill the rest of the matrix. 76 | for row in range(1, len(matrix)): 77 | for col in range(1, len(matrix[row])): 78 | sums[row][col] = sums[row - 1][col] + sums[row][col - 1] - sums[row -1][col - 1] + matrix[row][col] 79 | return sums 80 | -------------------------------------------------------------------------------- /Merge Linked Lists.py: -------------------------------------------------------------------------------- 1 | """ 2 | Merge Linked Lists 3 | Write a function that takes in the heads of two Singly Linked Lists that are in sorted order, respectively. The function should merge the lists in place 4 | (i.e., it shouldn't create a brand new list) and return the head of the merged list; the merged list should be in sorted order. 5 | Each LinkedList node has an integer value as well as a next node pointing to the next node in the list or to None / null if it's the tail of 6 | the list. 7 | You can assume that the input linked lists will always have at least one node; in other words, the heads will never be None / null. 8 | Sample Input 9 | headone = 2 -> 6 -> 7 -> 8 // the head node with value 2 10 | headTwo = 1 -> 3 -> 4 -> 5 -> 9 -> 10 // the head node with value 1 11 | Sample Output 12 | 1 -> 2 -> 3 -> 4 - 5 - 6 - 7 - 8 - 9 -> 10 // the new head node with value 1 13 | 14 | """ 15 | 16 | # SOLUTION 1 17 | 18 | class LinkedList: 19 | def __init__(self, value): 20 | self.value = value 21 | self.next = None 22 | 23 | # O(n + m) time 0(1) space - where n is the number of nodes in the first 24 | # Linked List and m is the number of nodes in the second Linked List 25 | def mergeLinkedLists(headOne, headTwo): 26 | p1 = headOne 27 | p1Prev = None 28 | p2 = headTwo 29 | while p1 is not None and p2 is not None: 30 | if p1.value < p2.value: 31 | p1Prev = p1 32 | p1 = p1.next 33 | else: 34 | if p1Prev is not None: 35 | p1Prev.next = p2 36 | p1Prev = p2 37 | p2 = p2.next 38 | p1Prev.next = p1 39 | if p1 is None: 40 | p1Prev.next = p2 41 | return headOne if headOne.value < headTwo.value else headTwo 42 | 43 | 44 | # SOLUTION 2 45 | 46 | class LinkedList: 47 | def __init__(self, value): 48 | self.value = value 49 | self.next = None 50 | 51 | # O(n + m) time | O(n + m) space - where n is the number of nodes in the first 52 | # Linked List and m is the number of nodes in the second Linked List 53 | def mergeLinkedLists(headOne, headTwo): 54 | recursiveMerge(headOne, headTwo, None) 55 | return headOne if headOne.value < headTwo.value else headTwo 56 | 57 | def recursiveMerge(p1, p2, p1Prev): 58 | if p1 is None: 59 | p1Prev.next = p2 60 | return 61 | if p2 is None: 62 | return 63 | 64 | if p1.value < p2.value: 65 | recursiveMerge(p1.next, p2, p1) 66 | else: 67 | if p1Prev is not None: 68 | p1Prev.next = p2 69 | newP2 = p2.next 70 | p2.next = p1 71 | recursiveMerge(p1, newP2, p2) 72 | 73 | -------------------------------------------------------------------------------- /Merge Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Merge Sort ☆ 3 | Write a function that takes in an array of integers and returns a sorted version of that array. Use the Merge Sort algorithm to sort the 4 | array. 5 | If you're unfamiliar with Merge Sort, we recommend watching the Conceptual Overview section of this question's video explanation 6 | before starting to code. 7 | 8 | Sample Input 9 | array = [8, 5, 2, 9, 5, 6, 3] 10 | 11 | Sample Output 12 | [2, 3, 5, 5, 6, 8, 9] 13 | 14 | """ 15 | 16 | # SOLUTION 1 17 | 18 | # Best: O(nlog(n)) time | O(nlog(n)) space 19 | # Average: (nlog(n)) time | O(nlog(n)) space 20 | # Worst: O(nlog(n)) time | O(nlog(n)) space 21 | def mergeSort(array): 22 | if len(array) == 1: 23 | return array 24 | middleIdx = len(array) // 2 25 | leftHalf = array[:middleIdx] 26 | rightHalf = array[middleIdx:] 27 | return mergeSortedArrays(mergeSort(leftHalf), mergeSort(rightHalf)) 28 | 29 | def mergeSortedArrays(leftHalf, rightHalf): 30 | sortedArray = [None] * (len(leftHalf) + len(rightHalf)) 31 | k = i = j = 0 32 | while i < len(leftHalf) and j < len(rightHalf): 33 | if leftHalf[i] <= rightHalf[j]: 34 | sortedArray[k] = leftHalf[i] 35 | i += 1 36 | else: 37 | sortedArray[k] = rightHalf[j] 38 | j += 1 39 | k += 1 40 | 41 | while i < len(leftHalf): 42 | sortedArray[k] = leftHalf[i] 43 | i += 1 44 | k += 1 45 | while j < len(rightHalf): 46 | sortedArray[k] = rightHalf[j] 47 | j += 1 48 | k += 1 49 | return sortedArray 50 | 51 | 52 | # SOLUTION 2 53 | 54 | # Best: O(nlog(n)) time | O(n) space 55 | # Average: 0(nlog(n)) time | O(n) space 56 | # Worst: O(nlog(n)) time | O(n) space 57 | def mergeSort(array): 58 | if len(array) <= 1: 59 | return array 60 | auxiliaryArray = array[:] 61 | mergeSortHelper (array, 0, len(array) - 1, auxiliaryArray) 62 | return array 63 | 64 | def mergeSortHelper(mainArray, startIdx, endIdx, auxiliaryArray): 65 | if startIdx == endIdx: 66 | return 67 | middleIdx = (startIdx + endIdx) // 2 68 | mergeSortHelper(auxiliaryArray, startIdx, middleIdx, mainArray) 69 | mergeSortHelper (auxiliaryArray, middleIdx + 1, endIdx, mainArray) 70 | doMerge(mainArray, startIdx, middleIdx, endIdx, auxiliaryArray) 71 | 72 | def doMerge(mainArray, startIdx, middleIdx, endIdx, auxiliaryArray): 73 | k = startIdx 74 | i = startIdx 75 | j = middleIdx + 1 76 | while i <= middleIdx and j <= endIdx: 77 | if auxiliaryArray[i] <= auxiliaryArray[j]: 78 | mainArray[k] = auxiliaryArray[i] 79 | i += 1 80 | else: 81 | mainArray[k] = auxiliaryArray[j] 82 | j += 1 83 | k += 1 84 | while i <= middleIdx: 85 | mainArray[k] = auxiliaryArray[i] 86 | i += 1 87 | k += 1 88 | while j <= endIdx: 89 | mainArray[k] = auxiliaryArray[j] 90 | j += 1 91 | k += 1 92 | 93 | -------------------------------------------------------------------------------- /Merging Overlapping Intervals.py: -------------------------------------------------------------------------------- 1 | """ 2 | Merging Overlapping Intervals 3 | 4 | Write a function that takes in a non-empty array of arbitrary intervals, merges any overlapping 5 | intervals, and returns the new intervals in no particular order. 6 | 7 | Each interval interval is an array of two integers,with interval[0] as the start of the interval 8 | and interval[1] as the end of the interval. 9 | 10 | Note tha back-to-back intervals aren't considered to be overlapping,For example, [1, 5] and 11 | [6, 7] aren't overlapping however, [1, 6] and [6, 7] are indeed overlapping. 12 | 13 | Also note that the start of any particular interval will always be less than or equal to the end 14 | of that interval 15 | 16 | Sample Input 17 | intervals = [[1 ,2], [3, 5], [4, 7], [6, 8], [9, 10]] 18 | 19 | Sample Output 20 | [[1, 2], [3, 8], [9, 10]] 21 | // Merge the intervals [3, 5], [4, 7], and [6, 8]. 22 | // The intervals could be ordered differently. 23 | """ 24 | 25 | # SOLUTION 1 26 | 27 | # O(log n) time | O(n) space 28 | def mergeOverlappingIntervals(intervals): 29 | intervals.sort() 30 | overlaps = [] 31 | previous = [None, None] 32 | for x, y in intervals: 33 | if previous[1] is not None and previous[1] >= x: 34 | previous[1] = previous[1] if previous[1] > y else y 35 | overlaps[-1] = previous 36 | continue 37 | overlaps.append([x, y]) 38 | previous = [x, y] 39 | return overlaps 40 | 41 | 42 | # SOLUTION 2 43 | 44 | # O(log n) time | O(n) space 45 | def mergeOverlappingIntervals(intervals): 46 | sortedIntervals = sorted(intervals, key=lambda x: x[0]) 47 | 48 | mergedIntervals = [] 49 | currentInterval = sortedIntervals[0] 50 | mergedIntervals.append(currentInterval) 51 | 52 | for nextInterval in sortedIntervals: 53 | _, currentIntervalsEnd = currentInterval 54 | nextIntervalStart, nextIntervalEnd = nextInterval 55 | 56 | if currentIntervalsEnd >= nextIntervalStart: 57 | currentInterval[1] = max(currentIntervalsEnd, nextIntervalEnd) 58 | else: 59 | currentInterval = nextInterval 60 | mergedIntervals.append(currentInterval) 61 | 62 | return mergedIntervals 63 | -------------------------------------------------------------------------------- /Min Max Stack Construction.py: -------------------------------------------------------------------------------- 1 | """ 2 | Min Max Stack Construction.py 3 | 4 | Write a MinMaxStack class for a Min Max Stack. The class should support: 5 | • Pushing and popping values on and off the stack. 6 | • Peeking at the value at the top of the stack. 7 | • Getting both the minimum and the maximum values in the stack at any given point in time. 8 | 9 | All class methods, when considered independently, should run in constant time and with constant space. 10 | 11 | Sample Usage 12 | // All operations below are performed sequentially. 13 | MinMaxStack(): - // instantiate a MinMaxStack 14 | push(5): - 15 | getMin(): 5 16 | getMax(): 5 17 | peek(): 5 18 | push(7): 19 | getMin(): 5 20 | getMax(): 7 21 | peek(): 7 22 | push(2): - 23 | getMin(): 2 24 | getMax(): 7 25 | peek(): 2 26 | pop(): 2 27 | pop(): 7 28 | getMin(): 5 29 | getMax(): 5 30 | peek(): 5 31 | 32 | """ 33 | 34 | 35 | # SOLUTION 1 36 | 37 | # Feel free to add new properties and methods to the class. 38 | class MinMaxStack: 39 | def __init__(self): 40 | self.minMaxStack = [] 41 | self.stack = [] 42 | 43 | # O(1) time | O(1) space 44 | def peek(self): 45 | return self.stack[len(self.stack) - 1] 46 | 47 | # O(1) time | O(1) space 48 | def pop(self): 49 | self.minMaxStack.pop() 50 | return self.stack.pop() 51 | 52 | # O(1) time | O(1) space 53 | def push(self, number): 54 | newMinMax = {'min': number, 'max': number} 55 | if len(self.minMaxStack): 56 | lastMinMax = self.minMaxStack[len(self.minMaxStack) - 1] 57 | newMinMax['min'] = min(lastMinMax['min'], number) 58 | newMinMax['max'] = max(lastMinMax['max'], number) 59 | self.minMaxStack.append(newMinMax) 60 | self.stack.append(number) 61 | 62 | # O(1) time | O(1) space 63 | def getMin(self): 64 | return self.minMaxStack[len(self.minMaxStack) - 1]['min'] 65 | 66 | # O(1) time | O(1) space 67 | def getMax(self): 68 | return self.minMaxStack[len(self.minMaxStack) - 1]['max'] 69 | -------------------------------------------------------------------------------- /Min Number of Coins For Change.py: -------------------------------------------------------------------------------- 1 | """ 2 | Min Number of Coins For Change 3 | 4 | Given an array of positive integers representing coin denominations and a single 5 | non-negative integer n representing a target amount of money.Write a function that 6 | returns the smallest number of coins needed to make change for (to sum up to) that 7 | target amount using the given coin denominators. 8 | 9 | Note that you have access to an unlimited amount of coins.In other words,if the 10 | denominators are [1, 5, 10], you have access to an unlimited amount of 1s, 5s and 10s 11 | 12 | If it's impossible to make change for the target amount, return -1. 13 | 14 | Sample Input 15 | n = 7 16 | denoms = [1, 5, 10] 17 | 18 | Sample Output 19 | 3 // 2x1 + 1x5 20 | """ 21 | 22 | # SOLUTION 1 23 | 24 | # time O(n*d) | space O(n) n is the length of string. 25 | # where d is the length of denoms 26 | def minNumberOfCoinsForChange(n, denoms): 27 | numOfCoins = [float('inf') for amount in range(n + 1)] 28 | numOfCoins[0] = 0 29 | for denom in denoms: 30 | for amount in range(len(numOfCoins)): 31 | if denom <= amount: 32 | numOfCoins[amount] = min(numOfCoins[amount], numOfCoins[amount - denom] + 1) 33 | 34 | return numOfCoins[n] if numOfCoins[n] != float('inf') else -1 35 | 36 | 37 | # SOLUTION 2 38 | 39 | # The below solution is greedy solution it doesn't work in all cases 40 | def minNumberOfCoinsForChange(n, denoms): 41 | cur = n 42 | denoms.sort(reverse=True) 43 | c = 0 44 | for i in denoms: 45 | while i <= cur: 46 | cur -= i 47 | c += 1 48 | return -1 if c == 0 else c 49 | -------------------------------------------------------------------------------- /Min Number of Jumps.py: -------------------------------------------------------------------------------- 1 | """ 2 | Min Number Of Jumps o♡ 3 | You're given a non-empty array of positive integers where each integer represents the maximum number 4 | of steps you can take forward in the array. For example, if the element at index 1 is 3 , you can go from 5 | index 1 to index 2, 3, or 4. 6 | Write a function that returns the minimum number of jumps needed to reach the final index. 7 | Note that jumping from index i to index i + x always constitutes one jump, no matter how large x is. 8 | 9 | Sample Input 10 | array = [3, 4, 2, 1, 2, 3, 7, 1, 1, 1, 3] 11 | 12 | Sample Output 13 | 4 // 3 --> (4 or 2) --> (2 or 3) --> 7 --> 3 14 | 15 | """ 16 | # SOLUTION 1 17 | 18 | # O(n^2) time | 0(n) space 19 | def minNumberOfJumps(array): 20 | jumps = [float("inf") for x in array] 21 | jumps [0] = 0 22 | for i in range(1, len(array)): 23 | for j in range(0, i): 24 | if array[j] >= i - j: 25 | jumps[i] = min(jumps[j] + 1, jumps[i]) 26 | return jumps[-1] 27 | 28 | # SOLUTION 2 29 | 30 | # O(n) time | 0(1) space 31 | def minNumberOfJumps(array): 32 | if len(array) == 1: 33 | return 0 34 | jumps = 0 35 | maxReach = array[0] 36 | steps = array[0] 37 | for i in range(1, len(array) - 1): 38 | maxReach = max(maxReach, i + array[i]) 39 | steps -= 1 40 | if steps == 0: 41 | jumps += 1 42 | steps = maxReach - i 43 | return jumps + 1 44 | 45 | -------------------------------------------------------------------------------- /Minimum Character For Words.py: -------------------------------------------------------------------------------- 1 | """ 2 | Minimum Character for Words 3 | 4 | Write a function that takes in an array of words and returns the smallest array of characters needed to form all of the 5 | words. The characters don't need to be in any particular order. 6 | 7 | For example, the characters ["y", "r", "0", "U"] are needed to form the words 8 | ["your", "you", "or", "yo"] . 9 | Note: the input words won't contain any spaces; however, they might contain punctuation and/or special characters. 10 | Sample Input 11 | words = ["this", "that", "did", "deed", "them!", "a"] 12 | 13 | Sample Output 14 | ["t", "t", "h", "i", "s", "a", "d", "d", "e", "e", "m", "!"] 15 | // The characters could be ordered differently. 16 | 17 | 18 | """ 19 | # SOLUTION 1 20 | 21 | # O(n * l) time | O(c) space - where n is the number of words,l is the length of the longest word, 22 | # and c is the number of unique characters across all words. 23 | # O(c) is lowerbound and O(n*l) is upperbound for the space complexity. 24 | 25 | def minimumCharactersForWords(words): 26 | maximumCharacterFrequencies = {} 27 | for word in words: 28 | characterFrequencies = countCharacterFrequencies(word) 29 | updateMaximumFrequencies(characterFrequencies, maximumCharacterFrequencies) 30 | return makeArrayFromCharacterFrequencies(maximumCharacterFrequencies) 31 | 32 | def countCharacterFrequencies(string): 33 | characterFrequencies = {} 34 | for character in string: 35 | if character not in characterFrequencies: 36 | characterFrequencies[character] = 0 37 | characterFrequencies[character] += 1 38 | return characterFrequencies 39 | 40 | def updateMaximumFrequencies(frequencies, maximumFrequencies): 41 | for character in frequencies: 42 | frequency = frequencies[character] 43 | if character in maximumFrequencies: 44 | maximumFrequencies[character] = max(frequency, maximumFrequencies[character]) 45 | else: 46 | maximumFrequencies[character] = frequency 47 | 48 | def makeArrayFromCharacterFrequencies(characterFrequencies): 49 | characters = [] 50 | for character in characterFrequencies: 51 | frequency = characterFrequencies[character] 52 | 53 | for _ in range(frequency): 54 | characters.append(character) 55 | return characters 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Minimum Waiting Time.py: -------------------------------------------------------------------------------- 1 | """ 2 | Minimum Waiting Time 3 | 4 | You're given a non-empty array of positive integers representing the amounts 5 | of time that specific queries take to execute.Only one query can be executed at 6 | a time,but the queries can be executed in any order. 7 | 8 | A query's waiting time is defined as the amount of time that it must wait before its 9 | execution starts.In other words,if a query is executed second,then its waiting time 10 | is the sum of the durations of the first query; if a query is executed third,then its 11 | waiting time is the sum of the durations of the first two queries. 12 | 13 | Write a function that returns the minimum amount of total waiting time for all of the 14 | queries.For example,if you're given the queries of durations [1,4,5],then the total 15 | waiting time if the queries were executed in the order of [5,1,4] would be 16 | (0) + (5) +(5 + 1) = 11.The first query of duration 5 would be executed immediately, 17 | so its waiting time would be 0,the second query of duration 1 would have to wait 18 | 5 seconds(the duration of the first query) to be executed and the last query would have 19 | to wait the duration of the first two queries before being executed 20 | 21 | Sample Input 22 | queries = [3 , 2, 1, 2, 6] 23 | 24 | Sample Output 25 | 17 26 | """ 27 | 28 | # SOLUTION 1 29 | 30 | # O(nlogn) time | O(1) space where n is the number of queries 31 | # O(nlogn + n) cancels out to O(nlogn) 32 | def minimumWaitingTime(queries): 33 | queries.sort() 34 | running_sum = 0 35 | prev_sum = 0 36 | for indx in range(len(queries) - 1): 37 | running_sum += queries[indx] 38 | prev_sum += running_sum 39 | 40 | return prev_sum 41 | 42 | # SOLUTION 2 43 | 44 | # O(nlogn) time | O(1) space where n is the number of queries 45 | def minimumWaitingTime(queries): 46 | queries.sort() 47 | total_waiting_time = 0 48 | for idx, duration in enumerate(queries): 49 | queries_left = len(queries) - (idx + 1) 50 | total_waiting_time += duration * queries_left 51 | 52 | return total_waiting_time 53 | 54 | 55 | """" 56 | example minimumWaitingTime1([1,4,5]) 57 | total_waiting_time 58 | = (1*(remaining_queries = 2) + 4*(remaining_queries = 1) + 5*(remaining_queries = 0)) 59 | = (2 + 4 + 0) 60 | = 6 61 | 62 | """ 63 | -------------------------------------------------------------------------------- /Monotonic Array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Monotonic Array 3 | 4 | Write a function that takes in array of integers and returns a boolean representing 5 | whether the array is monotonic. 6 | 7 | An array is said to be monotic if its elements, from left to right,are entirely 8 | non-increasing or entirely non-decreasing. 9 | 10 | Non-increasing elements aren't necessarily exclusively decreasing they simply don't 11 | increase.Similarly ,non-decreasing elements aren't necessarily increasing they simply 12 | don't decrease. 13 | 14 | Note that empty arrays and arrays of one element are monotonic. 15 | 16 | Sample Input 17 | array = [-1, -5, -10, -1100, -1100, -1101, -1102, -9001] 18 | 19 | Sample Output 20 | true 21 | """ 22 | 23 | # SOLUTION 1 24 | 25 | # time O(n) | space O(1) 26 | def isMonotonic(array): 27 | isnonedecreasing = True 28 | isnoneincreasing = True 29 | for i in range(1, len(array)): 30 | if array[i] > array[i - 1]: 31 | isnoneincreasing = False 32 | if array[i] < array[i - 1]: 33 | isnonedecreasing = False 34 | 35 | return isnonedecreasing or isnoneincreasing 36 | 37 | 38 | print(isMonotonic([0,0,0,0])) -------------------------------------------------------------------------------- /Move Element to the End.py: -------------------------------------------------------------------------------- 1 | """ 2 | Move Element to the End 3 | 4 | You're given an array of integers and an integer.Write a function that moves all 5 | instances of that integer in the array to the end of the array and returns the array. 6 | 7 | The function should perform this place (i.e it should mutate the input array) and 8 | doesn't need to maintain the order of the other integers. 9 | 10 | Sample Input 11 | array = [2, 1, 2 ,2 , 2, 3, 4, 2] 12 | toMove = 2 13 | 14 | Sample Output 15 | [1, 3, 4, 2, 2, 2, 2, 2] // the numbers 1,3, and 4 could be ordered differently. 16 | 17 | 18 | """ 19 | # SOLUTION 1 20 | 21 | # O(n) time | O(1) space where n is the length of array. 22 | def moveElementToEnd(array, toMove): 23 | i = 0 24 | j = len(array) - 1 25 | while i < j: 26 | while i < j and array[j] == toMove: 27 | j -= 1 28 | if array[i] == toMove: 29 | array[i], array[j] = array[j], array[i] 30 | i += 1 31 | return array 32 | 33 | 34 | # SOLUTION 2 35 | 36 | # O(n^2) time | O(1) space where n is the length of array. 37 | def moveElementToEnd(array, toMove): 38 | for i in array: 39 | if i == toMove: 40 | array.remove(i) 41 | array.append(i) 42 | return array 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Multi String Search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Multi String Search 3 | Write a function that takes in a big string and an array of small strings, all of which are smaller in length than the big 4 | string. The function should return an array of booleans, where each boolean represents whether the small string at that 5 | index in the array of small strings is contained in the big string. 6 | Note that you can't use language-built-in string-matching methods. 7 | 8 | Sample Input #1 9 | bigString = "this is a big string" 10 | smallStrings = ["this", "yo", "is", "a", "bigger", "string", "kappa"] 11 | 12 | Sample Output #1 13 | [true, false, true, true, false, true, false] 14 | 15 | Sample Input #2 16 | bigString = "abcdefghijklmnopqrstuvwxyz" 17 | smallStrings = ["abc", "mnopqr", "wyz", "no", "e", "tuuv"] 18 | 19 | Sample Output #2 20 | [true, true, false, true, true, false] 21 | 22 | """ 23 | 24 | # SOLUTION 1 25 | 26 | # O(bns) time | O(n) space 27 | def multiStringSearch(bigString, smallStrings): 28 | return [isInBigString(bigString, smallString) for smallString in smallStrings] 29 | 30 | 31 | def isInBigString(bigString, smallString): 32 | for i in range(len(bigString)): 33 | if i + len(smallString) > len(bigString): 34 | break 35 | if isInBigStringHelper(bigString, smallString, i): 36 | return True 37 | return False 38 | 39 | 40 | def isInBigStringHelper(bigString, smallString, startIdx): 41 | leftBigIdx = startIdx 42 | rightBigIdx = startIdx + len(smallString) - 1 43 | leftSmallIdx = 0 44 | rightSmallIdx = len(smallString) - 1 45 | while leftBigIdx <= rightBigIdx: 46 | if bigString[leftBigIdx] != smallString[leftSmallIdx] or bigString[rightBigIdx] != smallString[rightSmallIdx]: 47 | return False 48 | leftBigIdx += 1 49 | rightBigIdx -= 1 50 | leftSmallIdx += 1 51 | rightSmallIdx -= 1 52 | return True 53 | 54 | 55 | # SOLUTION 2 56 | 57 | # 0(b^2 + ns) time | 0(b^2 + n) space 58 | def multiStringSearch(bigString, smallStrings): 59 | modifiedSuffixTrie = ModifiedSuffixTrie(bigString) 60 | return [modifiedSuffixTrie.contains(string) for string in smallStrings] 61 | 62 | 63 | class ModifiedSuffixTrie: 64 | def __init__(self, string): 65 | self.root = {} 66 | self.populateModifiedSuffixTrieFrom(string) 67 | 68 | def populateModifiedSuffixTrieFrom(self, string): 69 | for i in range(len(string)): 70 | self.insertSubstringStartingAt(i, string) 71 | 72 | def insertSubstringStartingAt(self, i, string): 73 | node = self.root 74 | for j in range(i, len(string)): 75 | letter = string[j] 76 | if letter not in node: 77 | node[letter] = {} 78 | node = node[letter] 79 | 80 | def contains(self, string): 81 | node = self.root 82 | for letter in string: 83 | if letter not in node: 84 | return False 85 | node = node[letter] 86 | return True 87 | -------------------------------------------------------------------------------- /Next Greater Element.py: -------------------------------------------------------------------------------- 1 | """ 2 | Next Greater Element 3 | Write a function that takes in an array of integers and returns a new array containing, at each index, the next element 4 | in the input array that's greater than the element at that index in the input array. In other words, your function 5 | should return a new array where outputArray[i] is the next element in the input array that's greater than inputArray[i] 6 | .If there's no such next greater element for a particular index, the value at that index in the output array should be 7 | -1 . For example, given array = [1, 2] , your function should return [2, -1] . Additionally, your function should treat 8 | the input array as a circular array. A circular array wraps around itself as if it were connected end-to-end. 9 | 10 | So the next index after the last index in a circular array is the first index. This means that, for our problem, given 11 | array = [0, 0, 5, 0, 0, 3, 0 0] , the next greater element after 3 is 5 , since the array is circular. 12 | 13 | Sample Input 14 | array = [2, 5, -3, -4, 6, 7, 2] 15 | Sample Output 16 | [5, 6, 6, 6, 7, -1, 5] 17 | 18 | """ 19 | 20 | # SOLUTION 1 21 | 22 | # O(n) time | O(n) space - where n is the length of the array. 23 | def nextGreaterElement(array): 24 | result = [-1] * len(array) 25 | stack = [] 26 | for idx in range(2*len(array)): 27 | circularIdx = idx % len(array) 28 | 29 | while len(stack) > 0 and array[stack[len(stack) - 1]] < array[circularIdx]: 30 | top = stack.pop() 31 | result[top] = array[circularIdx] 32 | 33 | stack.append(circularIdx) 34 | 35 | return result 36 | 37 | 38 | # SOLUTION 2 39 | 40 | # O(n) time | O(n) space - where n is the length of the array. 41 | def nextGreaterElement(array): 42 | result = [-1] * len(array) 43 | stack = [] 44 | for idx in range(2*len(array) - 1, -1, -1): 45 | circularIdx = idx % len(array) 46 | 47 | while len(stack) > 0: 48 | if stack[len(stack) - 1] <= array[circularIdx]: 49 | stack.pop() 50 | else: 51 | result[circularIdx] =stack[len(stack) - 1] 52 | break 53 | stack.append(array[circularIdx]) 54 | return result 55 | 56 | 57 | # SOLUTION 3 58 | 59 | # O(n^2) time | O(n) space - where n is the length of the array. 60 | def nextGreaterElement(array): 61 | greater_than = [] 62 | size = len(array) 63 | for index in range(size): 64 | for nextindex in range(index + 1, 2*size): 65 | nextindex = nextindex % size 66 | if array[nextindex] > array[index]: 67 | greater_than.append(array[nextindex]) 68 | break 69 | elif index == nextindex: 70 | greater_than.append(-1) 71 | break 72 | return greater_than 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Node Depths.py: -------------------------------------------------------------------------------- 1 | """" 2 | Node's Depth 3 | 4 | The distance between a node in a Binary Tree and the tree's root is called the node's 5 | depths. 6 | Write a function that takes in a Binary Tree and returns the sum of its nodes' depths. 7 | 8 | Each BinaryTree node has an integer value, a left child node and a right child node. 9 | Children nodes can either be BinaryTree nodes themselves or None / null. 10 | 11 | Sample Input 12 | tree = 1 13 | / \ 14 | 2 3 15 | / \ / \ 16 | 4 5 6 7 17 | / \ 18 | 8 9 19 | 20 | Sample Output 21 | 16 22 | // The depth of the node with value 2 is 1. 23 | // The depth of the node with value 3 is 1 24 | // The depth of the node with value 4 is 2. 25 | // The depth of the node with value 5 is 2 26 | // The depth of the node with value 6 is 2. 27 | // The depth of the node with value 7 is 2 28 | // The depth of the node with value 8 is 3. 29 | // The depth of the node with value 9 is 3 30 | // Summing all of the depths yields 16 31 | """ 32 | 33 | # SOLUTION 1 34 | 35 | # This is the class of the input binary tree. 36 | class BinaryTree: 37 | def __init__(self, value): 38 | self.value = value 39 | self.left = None 40 | self.right = None 41 | 42 | 43 | # Running time O(n) , space O(h) where n is nodes number in a binary tree,h is node's height 44 | def nodeDepths(root, depth=0): 45 | if root is None: 46 | return 0 47 | return depth + nodeDepths(root.left, depth + 1) + nodeDepths(root.right, depth + 1) 48 | 49 | 50 | # SOLUTION 2 51 | 52 | # Running time O(n) , space O(h) where n is nodes number in a binary tree,h is node's height 53 | def nodeDepths(root): 54 | sumDepths = 0 55 | stack = [{"node": root, "depth": 0}] 56 | 57 | while len(stack) > 0: 58 | nodeInfo = stack.pop() 59 | node, depth = nodeInfo["node"], nodeInfo["depth"] 60 | if node is None: 61 | continue 62 | sumDepths += depth 63 | stack.append({"node": node.left, "depth": depth + 1}) 64 | stack.append({"node": node.right, "depth": depth + 1}) 65 | return sumDepths 66 | -------------------------------------------------------------------------------- /Node Swap.py: -------------------------------------------------------------------------------- 1 | """ 2 | Node Swap ☆ 3 | Write a function that takes in the head of a Singly Linked List, swaps every pair of adjacent nodes in place (i.e., doesn't create a brand 4 | new list), and returns its new head. 5 | If the input Linked List has an odd number of nodes, its final node should remain the same. 6 | Each LinkedList node has an integer value as well as a next node pointing to the next node in the list or to None / null if 7 | it's the tail of the list. 8 | You can assume that the input Linked List will always have at least one node; in other words, the head will never be None / null. 9 | 10 | Sample Input 11 | head = 0 -> 1 - 2 - 3 - 4 -> 5 // the head node with value a 12 | 13 | Sample Output 14 | 1 -> -> 3 -> 2 -> 5 -> 4 // the new head node with value 1 15 | 16 | """ 17 | 18 | # SOLUTION 1 19 | 20 | # This is an input class. Do not edit. 21 | class LinkedList: 22 | def __init__(self, value): 23 | self.value = value 24 | self.next = None 25 | 26 | 27 | # O(n) time | O(n) space where n is the number of nodes in the linked List 28 | def nodeSwap(head): 29 | if head is None or head.next is None: 30 | return head 31 | nextNode = head.next 32 | head.next = nodeSwap(head.next.next) 33 | nextNode.next = head 34 | return nextNode 35 | 36 | 37 | # SOLUTION 2 38 | 39 | # This is an input class. Do not edit. 40 | class LinkedList: 41 | def __init__(self, value): 42 | self.value = value 43 | self.next = None 44 | 45 | 46 | # O(n) time | 0(1) space - where n is the number of nodes in the Linked List 47 | def nodeSwap(head): 48 | tempNode = LinkedList(0) 49 | tempNode.next = head 50 | 51 | prevNode = tempNode 52 | while prevNode.next is not None and prevNode.next.next is not None: 53 | firstNode = prevNode.next 54 | secondNode = prevNode.next.next 55 | # prevNode -> firstNode -> secondNode -> X 56 | 57 | firstNode.next = secondNode.next 58 | secondNode.next = firstNode 59 | prevNode.next = secondNode 60 | # prevNode -> secondNode -> firstNode -> X 61 | 62 | prevNode = firstNode 63 | return tempNode.next 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Non-Constructible Change.py: -------------------------------------------------------------------------------- 1 | """ 2 | Non-Constructible Change 3 | 4 | Given an array of positive integers representing the values of coins in your 5 | possession,write a function that returns the minimum amount of change(the minimum sum 6 | of money) that you cannot create.The given coins can have any positive integer value 7 | and aren't necessarily unique (i.e you can have multiple coins and of the same value). 8 | 9 | For example,if you're given coins = [1, 2, 5], the minimum amount of change that you 10 | can't create is 4. If you're given no coins,the minimum amount of change that you can't 11 | create is 1. 12 | 13 | Sample Input 14 | coins = [5, 7, 1, 1, 2, 3 , 22] 15 | 16 | Sample Output 17 | 20 18 | 19 | """ 20 | 21 | # SOLUTION 1 22 | 23 | # O(nlogn) time | O(1) space where n is the number of coins 24 | def nonConstructibleChange(coins): 25 | coins.sort() 26 | 27 | currentChangeCreated = 0 28 | for coin in coins: 29 | if coin > currentChangeCreated + 1: 30 | return currentChangeCreated + 1 31 | 32 | currentChangeCreated += coin 33 | 34 | return currentChangeCreated + 1 35 | 36 | 37 | -------------------------------------------------------------------------------- /Nth Fibonacci.py: -------------------------------------------------------------------------------- 1 | """ 2 | Nth Fibonacci number 3 | 4 | The Fibonacci sequence is defined as follows the first number of the sequence is 0, 5 | the second number of the sequence is 1, and the nth number is the sum of the (n-1)th 6 | and (n-2)th numbers.Write a function that takes in an integer n and returns the nth 7 | Fibonacci number. 8 | 9 | Important note: the Fibonacci sequence is often defined with its first two numbers 10 | as F0 = 0 and F1 = 1. For the purpose of this question,the first Fibonacci number is 11 | F0; therefore getNthFib(1) is equal to F0, getNthFib(2) is equal to F1, etc... 12 | 13 | Sample Input #1 14 | n = 2 15 | Sample Output #1 16 | 1 // 0,1 17 | 18 | Sample Input #2 19 | n = 6 20 | Sample Output #2 21 | 5 // 0, 1, 1, 2, 3,5 22 | 23 | """ 24 | 25 | # SOLUTION 1 26 | 27 | # Running time O(2^n) space O(n) 28 | def getNthFib(n): 29 | if n == 2: 30 | return 1 31 | elif n == 1: 32 | return 0 33 | else: 34 | return getNthFib(n - 1) + getNthFib(n - 2) 35 | 36 | # SOLUTION 2 37 | 38 | # Running time O(n) space O(n) 39 | def getNthFib(n, memoize={1: 0, 2: 1}): 40 | if n in memoize: 41 | return memoize[n] 42 | else: 43 | memoize[n] = getNthFib(n - 1, memoize) + getNthFib(n - 2, memoize) 44 | return memoize[n] 45 | 46 | 47 | # SOLUTION 3 48 | 49 | # Running time O(n) space O(1) 50 | def getNthFib(n): 51 | lastTwo = [0, 1] 52 | counter = 3 53 | while counter <= n: 54 | nextFib = lastTwo[0] + lastTwo[1] 55 | lastTwo[0], lastTwo[1] = lastTwo[1], nextFib 56 | counter += 1 57 | 58 | return lastTwo[1] if n > 1 else lastTwo[0] 59 | -------------------------------------------------------------------------------- /Number Of Binary Tree Topoligies.py: -------------------------------------------------------------------------------- 1 | """ 2 | Number Of Binary Tree Topologies 3 | Write a function that takes in a non-negative integer n and returns the number of possible Binary Tree topologies 4 | that can be created with exactly n nodes. 5 | 6 | A Binary Tree topology is defined as any Binary Tree configuration, irrespective of node values. For instance, 7 | there exist only two Binary Tree topologies when n is equal to 2 :a root node with a left node, and a root node with a right node. 8 | Note that when n is equal to there's one topology that can be created: the None / null node. 9 | 10 | Sample Input 11 | n = 3 12 | Sample Output 13 | 5 14 | 15 | """ 16 | # SOLUTION 1 17 | 18 | # Upper Bound: 0((n* (2n)!)/(n!(n+1)!)) time | O(n) space 19 | def numberOfBinaryTreeTopologies(n): 20 | if n == 0: 21 | return 1 22 | numberOfTrees = 0 23 | for leftTreeSize in range(n): 24 | rightTreeSize = n - 1 - leftTreeSize 25 | numberOfLeftTrees = numberOfBinaryTreeTopologies(leftTreeSize) 26 | numberOfRightTrees = numberOfBinaryTreeTopologies (rightTreeSize) 27 | numberOfTrees += numberOfLeftTrees * numberOfRightTrees 28 | return numberOfTrees 29 | 30 | # SOLUTION 2 31 | 32 | # O(n^2) time | (n) space 33 | def numberOfBinaryTreeTopologies(n, cache={0: 1}): 34 | if n in cache: 35 | return cache[n] 36 | numberOfTrees = 0 37 | for leftTreeSize in range(n): 38 | rightTreeSize = n - 1 - leftTreeSize 39 | numberOfLeftTrees = numberOfBinaryTreeTopologies(leftTreeSize, cache) 40 | numberOfRightTrees = numberOfBinaryTreeTopologies(rightTreeSize, cache) 41 | numberOfTrees += numberOfLeftTrees * numberOfRightTrees 42 | cache[n] = numberOfTrees 43 | return numberOfTrees 44 | 45 | # SOLUTION 3 46 | 47 | # O(n^2) time | O(n) space 48 | def numberOfBinaryTreeTopologies(n): 49 | cache = [1] 50 | for m in range(1, n + 1): 51 | numberOfTrees = 0 52 | for leftTreeSize in range(m): 53 | rightTreeSize = m - 1 - leftTreeSize 54 | numberOfLeftTrees = cache[leftTreeSize] 55 | numberOfRightTrees = cache[rightTreeSize] 56 | numberOfTrees += numberOfLeftTrees * numberOfRightTrees 57 | cache.append(numberOfTrees) 58 | return cache[n] 59 | 60 | 61 | -------------------------------------------------------------------------------- /Number of Ways To Make Change.py: -------------------------------------------------------------------------------- 1 | """ 2 | Number of Ways To Make Change 3 | 4 | Given an array of distinct positive integers representing coin denominations 5 | and a single non-negative integer n representing a target amount of money.Write 6 | a function that returns the number of ways to make change for that target amount 7 | using the given coin denominations. 8 | 9 | Note that an unlimited amount of coins is at your disposal. 10 | 11 | Sample Input 12 | n = 6 13 | denoms = [1, 5] 14 | 15 | Sample Output 16 | 2 // 1x1 + 1x5 and 6x1 17 | """ 18 | 19 | # SOLUTION 1 20 | 21 | # O(nd) time | O(n) space where d is the number of denoms 22 | def numberOfWaysToMakeChange(n, denoms): 23 | ways = [0 for amount in range(n + 1)] 24 | ways[0] = 1 25 | for denom in denoms: 26 | for amount in range(1, n+1): 27 | if denom <= amount: 28 | ways[amount] += ways[amount - denom] 29 | 30 | return ways[n] 31 | 32 | -------------------------------------------------------------------------------- /Number of Ways To Traverse Graph.py: -------------------------------------------------------------------------------- 1 | """ 2 | Number of Ways To Traverse Graph 3 | 4 | You're given two positive integers representing the width and the height of a grid-shaped,rectangular 5 | graph.Write a function that returns the number of ways to reach the bottom right corner of the graph 6 | when starting at the top left corner.Each move you take must either go down or right.In other words,you 7 | can never move up or left in the graph. 8 | 9 | For example,given the graph illustrated below, with width = 2 and height =3, there are three ways to reach 10 | the bottom right corner when starting at the top left corner 11 | _ _ 12 | |_|_| 13 | |_|_| 14 | |_|_| 15 | 16 | 1. Down, Down, Right 17 | 2. Right, Down, Down 18 | 3. Down,Right, Down 19 | 20 | Note: you may assume that width * height >= 2.In other words, the graph will never be a 1X1 grid 21 | 22 | Sample Input 23 | width = 4 24 | height = 3 25 | 26 | Sample Output 27 | 10 28 | """ 29 | 30 | # SOLUTION 1 31 | 32 | # O(2^(n + m)) time | O(n + m) space where n is the width of the graph and 33 | # m is the height 34 | def numberOfWaysToTraverseGraph(width, height): 35 | if width == 1 or height == 1: 36 | return 1 37 | return numberOfWaysToTraverseGraph(width - 1, height) + numberOfWaysToTraverseGraph(width, height - 1) 38 | 39 | 40 | # SOLUTION 2 41 | 42 | # O(n * m) time | O(n * m) space where n is the width of the graph and 43 | # m is the height 44 | def numberOfWaysToTraverseGraph(width, height): 45 | numberOfWays = [[0 for _ in range(width + 1)] for _ in range(height + 1)] 46 | for widthIdx in range(1, width + 1): 47 | for heightIdx in range(1, height + 1): 48 | if widthIdx == 1 or heightIdx == 1: 49 | numberOfWays[heightIdx][widthIdx] = 1 50 | else: 51 | waysLeft = numberOfWays[heightIdx][widthIdx - 1] 52 | waysUp = numberOfWays[heightIdx - 1][widthIdx] 53 | numberOfWays[heightIdx][widthIdx] = waysUp + waysLeft 54 | 55 | return numberOfWays[height][width] 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Numbers in PI.py: -------------------------------------------------------------------------------- 1 | """ 2 | Numbers In Pi ☆ 3 | Given a string representation of the first n digits of Pi and a list of positive integers (all in string format), 4 | write a function that returns the smallest 5 | number of spaces that can be added to the n digits of Pi such that all resulting numbers are found in the list of integers. 6 | Not e that a single number can appear multiple times in the resulting numbers. For example, if Pi is "3141" and the numbers are 7 | ["1", "3", "4"] , the number "1" is allowed to appear twice in the list of resulting numbers after three spaces are added: 8 | "3 | 1 | 4 | 1" 9 | If no number of spaces to be added exists such that all resulting numbers are found in the list of integers, the function should return -1. 10 | 11 | Sample Input 12 | pi = "3141592653589793238462643383279", 13 | numbers = ["314159265358979323846", "26433", "8", "3279", "314159265", "35897932384626433832", "79"] 14 | 15 | Sample Output 16 | 2 // "314159265 | 35897932384626433832 | 79" 17 | 18 | """ 19 | # SOLUTION 1 20 | 21 | # O(n^3 + m) time | O(n + m) space - where n is the number of digits in Pi and m is the number of favorite numbers 22 | def numbersInPi(pi, numbers): 23 | numbersTable = {number: True for number in numbers} 24 | minSpaces = getMinSpaces (pi, numbersTable, {}, 0) 25 | return -1 if minSpaces == float("inf") else minSpaces 26 | 27 | def getMinSpaces(pi, numbersTable, cache, idx): 28 | if idx == len(pi): 29 | return -1 30 | if idx in cache: 31 | return cache[idx] 32 | minSpaces = float("inf") 33 | for i in range(idx, len(pi)): 34 | prefix = pi[idx : i + 1] 35 | if prefix in numbersTable: 36 | minSpacesInSuffix = getMinSpaces(pi, numbersTable, cache, i + 1) 37 | minSpaces = min(minSpaces, minSpacesInSuffix + 1) 38 | cache[idx] = minSpaces 39 | return cache[idx] 40 | 41 | # SOLUTION 2 42 | 43 | # O(n^3 + m) time | O(n + m) space where n is the number of digits in Pi and m is the number of favorite numbers 44 | def numbersInPi(pi, numbers): 45 | numbersTable = {number: True for number in numbers} 46 | cache = {} 47 | for i in reversed(range(len(pi))): 48 | getMinSpaces(pi, numbersTable, cache, i) 49 | return -1 if cache[0] == float("inf") else cache[0] 50 | 51 | def getMinSpaces(pi, numbersTable, cache, idx): 52 | if idx == len(pi): 53 | return -1 54 | if idx in cache: 55 | return cache[idx] 56 | minSpaces = float("inf") 57 | for i in range(idx, len(pi)): 58 | prefix = pi[idx : i + 1] 59 | if prefix in numbersTable: 60 | minSpacesInSuffix = getMinSpaces(pi, numbersTable, cache, i + 1) 61 | minSpaces = min(minSpaces, minSpacesInSuffix + 1) 62 | cache[idx] = minSpaces 63 | return cache[idx] 64 | 65 | -------------------------------------------------------------------------------- /Palindrome Check.py: -------------------------------------------------------------------------------- 1 | """ 2 | Palindrome Check 3 | 4 | Write a function that takes in a non-empty string and tha returns a boolean representing 5 | whether the string is a palindrome. 6 | 7 | A palindrome is defined as a string that's written the same forward and backward.Note 8 | that single-character strings are palindromes. 9 | 10 | Sample Input 11 | string = 'abcdcba' 12 | 13 | Sample Output 14 | true // it's written the same forward and backward 15 | 16 | 17 | """ 18 | # SOLUTION 1 19 | 20 | # Running time O(n) | space 0(1) 21 | def isPalindrome(string): 22 | left = 0 23 | right = len(string) - 1 24 | 25 | while left < right: 26 | if string[left] != string[right]: 27 | return False 28 | left += 1 29 | right -= 1 30 | return True 31 | 32 | # SOLUTION 2 33 | 34 | # Running time O(n^2) | space 0(1) 35 | def isPalindrome(string): 36 | reversedstring = "" 37 | for i in reversed(range(len(string))): 38 | reversedstring += string[i] 39 | return string == reversedstring 40 | 41 | # SOLUTION 3 42 | 43 | # Running time O(n) | space 0(n) 44 | def isPalindrome(string): 45 | reversedchars = [] 46 | for i in reversed(range(len(string))): 47 | reversedchars.append(string[i]) 48 | return string == "".join(reversedchars) 49 | 50 | # SOLUTION 4 51 | 52 | # Running time O(n) | space 0(n) 53 | def isPalindrome(string, i=0): 54 | j = len(string) - 1 - i 55 | return True if i >= j else string[i] == string[j] and isPalindrome(string, i+1) 56 | 57 | # SOLUTION 5 58 | 59 | # Below we are using Tail Recursion 60 | # Running time O(n) | space 0(1) 61 | def isPalindrome(string, i=0): 62 | j = len(string) - 1 - i 63 | if i >= j: 64 | return True 65 | if string[i] != string[j]: 66 | return False 67 | return isPalindrome(string, i+1) 68 | 69 | 70 | -------------------------------------------------------------------------------- /Palindrome Partitioning Min Cuts.py: -------------------------------------------------------------------------------- 1 | """ 2 | Palindrome Partitioning Min Cuts o ☆ 3 | Given a non-empty string, write a function that returns the minimum number of cuts needed to perform on the string 4 | such that each remaining substring is a palindrome. 5 | A palindrome is defined as a string that's written the same forward as backward. Note that single-character strings are 6 | palindromes. 7 | 8 | Sample Input 9 | string = "noonabbad" 10 | 11 | Sample Output 12 | 2 // noon | abba | d" 13 | 14 | """ 15 | 16 | # SOLUTION 1 17 | 18 | # O(n^3) time | O(n^2) space 19 | def palindromePartitioningMinCuts(string): 20 | palindromes = [[False for i in string] for j in string] 21 | for i in range(len(string)): 22 | for j in range(i, len(string)): 23 | palindromes[i][j] = ispalindrome(string[i: j + 1]) 24 | cuts = [float("inf") for i in string] 25 | for i in range(len(string)): 26 | if palindromes[0][i]: 27 | cuts[i] = 0 28 | else: 29 | cuts[i] = cuts[i - 1] + 1 30 | for j in range(1, i): 31 | if palindromes[j][i] and cuts[j - 1] + 1 < cuts[i]: 32 | cuts[i] = cuts[j - 1] + 1 33 | return cuts[-1] 34 | 35 | 36 | def ispalindrome(string): 37 | leftIdx = 0 38 | rightIdx = len(string) - 1 39 | while leftIdx < rightIdx: 40 | if string[leftIdx] != string[rightIdx]: 41 | return False 42 | leftIdx += 1 43 | rightIdx -= 1 44 | return True 45 | 46 | 47 | # SOLUTION 2 48 | 49 | # O(n^2) time | O(n^2) space 50 | def palindromePartitioningMinCuts(string): 51 | palindromes = [[False for i in string] for j in string] 52 | for i in range(len(string)): 53 | palindromes[i][i] = True 54 | for length in range(2, len(string) + 1): 55 | for i in range(0, len(string) - length + 1): 56 | j = i + length - 1 57 | if length == 2: 58 | palindromes[i][j] = string[i] == string[j] 59 | else: 60 | palindromes[i][j] = string[i] == string[j] and palindromes[i + 1][j - 1] 61 | 62 | cuts = [float("inf") for i in string] 63 | for i in range(len(string)): 64 | if palindromes[0][i]: 65 | cuts[i] = 0 66 | else: 67 | cuts[i] = cuts[i - 1] + 1 68 | for j in range(1, i): 69 | if palindromes[j][i] and cuts[j - 1] + 1 < cuts[i]: 70 | cuts[i] = cuts[j - 1] + 1 71 | return cuts[-1] 72 | -------------------------------------------------------------------------------- /Pattern Matcher.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pattern Matcher O ☆ 3 | You're given two non-empty strings. The first one is a pattern consisting of only "x" sand / or "y" s; the other one is a normal string 4 | of alphanumeric characters. Write a function that checks whether the normal string matches the pattern. 5 | A string so is said to match a pattern if replacing all "x" s in the pattern with some non-empty substring si of se and replacing all 6 | "y" s in the pattern with some non-empty substring S2 of se yields the same string se . 7 | If the input string doesn't match the input pattern, the function should return an empty array, otherwise, it should return an array 8 | holding the strings si and s2 that represent "x" and "y" in the normal string, in that order. If the pattern doesn't contain any 9 | "x" sor "y" s, the respective letter should be represented by an empty string in the final array that you return. 10 | You can assume that there will never be more than one pair of strings si and s2 that appropriately represent "x" and "y" in the 11 | normal string. 12 | Sample Input 13 | pattern = "xxyxxy" 14 | string = "gogopowerrangergogopowerranger" 15 | Sample Output 16 | ["go", "powerranger"] 17 | 18 | """ 19 | # SOLUTION 1 20 | 21 | # 0(n^2 + m) time | O(n + m) space 22 | def patternMatcher(pattern, string): 23 | if len(pattern) > len(string): 24 | return [] 25 | newPattern = getNewPattern(pattern) 26 | didSwitch = newPattern[0] != pattern[0] 27 | counts = {"x": 0, "y": 0} 28 | firstYPos = getCountsAndFirstYPos(newPattern, counts) 29 | if counts["y"] != 0: 30 | for lenOfX in range(1, len(string)): 31 | lenOfY = (len(string) - lenOfX * counts["x"]) / counts["y"] 32 | if lenOfY <= 0 or lenOfY % 1 != 0: 33 | continue 34 | lenOfY = int(lenOfY) 35 | yIdx = firstYPos * lenOfX 36 | x = string[:lenOfX] 37 | y = string[yIdx : yIdx + lenOfY] 38 | potentialMatch = map(lambda char: x if char == "x" else y, newPattern) 39 | if string == "".join(potentialMatch): 40 | return [x, y] if not didSwitch else [y, x] 41 | else: 42 | lenOfX = len(string) / counts["x"] 43 | if lenOfX % 1 == 0: 44 | lenOfX = int(lenOfX) 45 | x = string[:lenOfX] 46 | potentialMatch = map(lambda char: x, newPattern) 47 | if string == "".join(potentialMatch): 48 | return [x, ""] if not didSwitch else ["", x] 49 | return [] 50 | 51 | def getNewPattern(pattern): 52 | patternLetters = list(pattern) 53 | if pattern[0] == "x": 54 | return patternLetters 55 | else: 56 | return list(map(lambda char: "x" if char == "y" else "y", patternLetters)) 57 | 58 | def getCountsAndFirstYPos(pattern, counts): 59 | firstYPos = None 60 | for i, char in enumerate(pattern): 61 | counts[char] += 1 62 | if char == "y" and firstYPos is None: 63 | firstYPos = i 64 | return firstYPos 65 | -------------------------------------------------------------------------------- /Permutations.py: -------------------------------------------------------------------------------- 1 | """ 2 | Write a function that takes in an array of unique integers and returns an array 3 | of all permutations of those integers in no particular order. 4 | 5 | If the input array is empty,the function should return an empty array 6 | 7 | Sample Input 8 | array = [1, 2, 3] 9 | 10 | Sample Output 11 | [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] 12 | 13 | 14 | """ 15 | 16 | # SOLUTION 1 17 | 18 | # Upper Bound: O(n^2*n!) time | O(n*n!) space 19 | # Roughly: O(n*n!) time | O(n*n!) space 20 | 21 | def getPermutations(array): 22 | permutations = [] 23 | permutationsHelper(array, [], permutations) 24 | return permutations 25 | 26 | def permutationsHelper(array, currentPermutation, permutations): 27 | if not len(array) and len(currentPermutation): 28 | permutations.append(currentPermutation) 29 | else: 30 | for i in range(len(array)): 31 | newArray = array[:i] + array[i + 1:] 32 | newPermutation = currentPermutation + [array[i]] 33 | permutationsHelper(newArray, newPermutation, permutations) -------------------------------------------------------------------------------- /Powerset.py: -------------------------------------------------------------------------------- 1 | """ 2 | Powerset 3 | 4 | Write a function that takes in an array of unique integers and returns its powerset. 5 | 6 | The powerset P(X) of a set X is the set of all subsets of X.For example ,the powerset 7 | of [1, 2] is [[], [1], [2], [1,2]]. 8 | 9 | Note that the sets in the powerset do not need to be in any particular order. 10 | 11 | Sample Input 12 | array = [1, 2, 3] 13 | 14 | Sample Output 15 | [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]] 16 | 17 | """ 18 | 19 | # SOLUTION 1 20 | 21 | # time O(n^2^n) | space O(n^2^n ) 22 | def powerset(array): 23 | subsets = [[]] 24 | for ele in array: 25 | for i in range(len(subsets)): 26 | currentSubset = subsets[i] 27 | subsets.append(currentSubset + [ele]) 28 | return subsets 29 | 30 | 31 | # SOLUTION 2 32 | 33 | # time O(n^2^n) | space O(n^2^n ) 34 | def powerset(array, idx=None): 35 | if idx is None: 36 | idx = len(array) - 1 37 | if idx < 0: 38 | return [[]] 39 | ele = array[idx] 40 | subsets = powerset(array, idx-1) 41 | for i in range(len(subsets)): 42 | currentSubset = subsets[i] 43 | subsets.append(currentSubset + [ele]) 44 | return subsets 45 | 46 | -------------------------------------------------------------------------------- /Product Sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | Product Sum 3 | 4 | Write a function that takes in a "special" array and return its product sum 5 | 6 | A "special" array is a none-empty array that contains either integers or other 7 | "special" arrays.The product sum of a "special" array is the sum of its elements,where 8 | "special" arrays inside it are summed themselves and then multiplied by their level 9 | of depth. 10 | 11 | The depth of "special" array is how far nested it is.For instance,the depth of [] is 1; 12 | the depth of the inner array in [[]] is 2; the depth of the innermost array in [[[]]] is 13 | 3. 14 | 15 | Therefore, the product sum of [x,y] is x + y; the product sum of [x, [y, z]] 16 | is x + 2 * (y+z); the product sum of [x, [y, [z]]] is x +2 * (y+ 3z). 17 | 18 | Sample Input 19 | 20 | array = [5, 2, [7,-1], 3, [6, [-13, 8], 4]] 21 | 22 | Sample Output 23 | 12 // calculated as : 5 + 2 + 2 * (7 - 1) + 3 + 2 * (6 + 3 * (-13+8) + 4) 24 | 25 | """ 26 | # SOLUTION 1 27 | 28 | # Running time O(n) | space 0(d) where n is total number of elements including 29 | # sub-elements, and d is the greatest depth of special arrays in the array. 30 | def productSum(array, depth=1): 31 | sums = 0 32 | for elem in array: 33 | if isinstance(elem, list): 34 | sums += productSum(elem, depth + 1) 35 | else: 36 | sums += elem 37 | return depth * sums 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Quick Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Quick Sort ☆ 3 | Write a function that takes in an array of integers and returns a sorted version of that array. Use the Quick Sort 4 | algorithm to sort the array. 5 | If you're unfamiliar with Quick Sort, we recommend watching the Conceptual Overview section of this question's video 6 | explanation before starting to code. 7 | 8 | Sample Input 9 | array = [8, 5, 2, 9, 5, 6, 3] 10 | Sample Output 11 | [2, 3, 5, 5, 6, 8, 9] 12 | 13 | """ 14 | 15 | # SOLUTION 1 16 | 17 | # Best: O(nlog(n)) time | O(log(n)) space 18 | # Average: 0(nlog(n)) time | O(log(n)) space 19 | # Worst: O(n^2) time | 0(log(n)) space 20 | def quickSort(array): 21 | quickSortHelper(array, 0, len(array) - 1) 22 | return array 23 | 24 | def quickSortHelper(array, startIdx, endIdx): 25 | if startIdx >= endIdx: 26 | return 27 | pivotIdx = startIdx 28 | leftIdx = startIdx + 1 29 | rightIdx = endIdx 30 | while rightIdx >= leftIdx: 31 | if array[leftIdx] > array[pivotIdx] > array[rightIdx]: 32 | swap(leftIdx, rightIdx, array) 33 | if array[leftIdx] <= array[pivotIdx]: 34 | leftIdx += 1 35 | if array[rightIdx] >= array[pivotIdx]: 36 | rightIdx -= 1 37 | swap(pivotIdx, rightIdx, array) 38 | leftSubarrayIsSmaller = rightIdx - 1 - startIdx < endIdx - (rightIdx + 1) 39 | if leftSubarrayIsSmaller: 40 | quickSortHelper(array, startIdx, rightIdx - 1) 41 | quickSortHelper(array, rightIdx + 1, endIdx) 42 | else: 43 | quickSortHelper(array, rightIdx + 1, endIdx) 44 | quickSortHelper(array, startIdx, rightIdx - 1) 45 | 46 | 47 | def swap(i, j, array): 48 | array[i], array[j] = array[j], array[i] 49 | -------------------------------------------------------------------------------- /Quickselect.py: -------------------------------------------------------------------------------- 1 | """ 2 | Quickselect ☆ 3 | Write a function that takes in an array of distinct integers as well as an integer k and that returns the kth 4 | smallest integer in that array. 5 | The function should do this in linear time, on average. 6 | 7 | Sample Input 8 | array = [8, 5, 2, 9, 7, 6, 3] 9 | k = 3 10 | 11 | Sample Output 12 | 5 13 | 14 | """ 15 | 16 | # SOLUTION 1 17 | 18 | # Best: O(n) time | 0(1) space 19 | # Average: O(n) time | 0(1) space 20 | # Worst: O(n^2) time | 0(1) space 21 | def quickselect(array, k): 22 | position = k - 1 23 | return quickselectHelper (array, 0, len(array) - 1, position) 24 | 25 | 26 | def quickselectHelper(array, startIdx, endIdx, position): 27 | while True: 28 | if startIdx > endIdx: 29 | raise Exception("Your algorithm should never arrive here!") 30 | pivotIdx = startIdx 31 | leftIdx = startIdx + 1 32 | rightIdx = endIdx 33 | while leftIdx <= rightIdx: 34 | if array[leftIdx] > array[pivotIdx] > array[rightIdx]: 35 | swap(leftIdx, rightIdx, array) 36 | if array[leftIdx] <= array[pivotIdx]: 37 | leftIdx += 1 38 | if array[rightIdx] >= array[pivotIdx]: 39 | rightIdx -=1 40 | swap(pivotIdx, rightIdx, array) 41 | if rightIdx == position: 42 | return array[rightIdx] 43 | elif rightIdx < position: 44 | startIdx = rightIdx + 1 45 | else: 46 | endIdx = rightIdx - 1 47 | 48 | def swap(one, two, array): 49 | array[one], array[two] = array[two], array[one] 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coding Intreview Preparation 2 | I created this Github repo to share my day to day progress on solving coding interview questions so as to improve my Problem Solving skills, 3 | I try to practise on daily basis,sometimes i lack motivation but i still push on. 4 | -------------------------------------------------------------------------------- /Radix Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Radix Sort 3 | Write a function that takes in an array of non-negative integers and returns a sorted version of that array. 4 | Use the Radix Sort algorithm to sort the array. 5 | 6 | If you're unfamiliar with Radix Sort, we recommend watching the Conceptual Overview section of this 7 | question's video explanation before starting to code. 8 | 9 | Sample Input 10 | array = [8762, 654, 3008, 345, 87, 65, 234, 12, 2] 11 | 12 | Sample Output 13 | [2, 12, 65, 87, 234, 345, 654, 3008, 8762] 14 | 15 | """ 16 | 17 | # SOLUTION 1 18 | 19 | # O(d*(n + b)) time | 0(n + b) space where n is the length of the input array, 20 | # d is the max number of digits, and b is the base of the numbering system used 21 | def radixSort(array): 22 | if len(array) == 0: 23 | return array 24 | maxNumber = max(array) 25 | 26 | digit = 0 27 | while maxNumber / 10 ** digit > 0: 28 | countingSort(array, digit) 29 | digit += 1 30 | 31 | return array 32 | 33 | def countingSort(array, digit): 34 | sortedArray = [0] * len(array) 35 | countArray = [0] * 10 36 | 37 | digitColumn = 10 ** digit 38 | for num in array: 39 | countIndex = (num // digitColumn) % 10 40 | countArray[countIndex] += 1 41 | 42 | for idx in range(1, 10): 43 | countArray[idx] += countArray[idx - 1] 44 | 45 | for idx in range(len(array) - 1, -1, -1): 46 | countIndex = (array[idx] // digitColumn) % 10 47 | countArray[countIndex] -= 1 48 | sortedIndex = countArray[countIndex] 49 | sortedArray[sortedIndex] = array[idx] 50 | 51 | for idx in range(len(array)): 52 | array[idx] = sortedArray[idx] 53 | -------------------------------------------------------------------------------- /Remove Duplicates From a Linked List.py: -------------------------------------------------------------------------------- 1 | """ 2 | Remove Duplicates From a Linked List 3 | 4 | You're given the head of Singly Linked List whose nodes are in sorted order with 5 | respect to their values. Write a function that returns a modified version of the 6 | Linked List that doesn't contain any nodes with duplicate values.The Linked List 7 | should be modified in place(i.e you shouldn't create a brand new list),and the 8 | modified LInked List should still have its nodes sorted with respect to their values. 9 | 10 | Each LinkedList node has an integer value as well as a next node pointing to the 11 | next node in the list or to None / null if it's the tail of the list. 12 | 13 | Sample Input 14 | linkedList = 1 -> 1 -> 3 -> 4 -> 4 -> 4 -> 5 -> 6 -> 6 // the head node with value 1 15 | 16 | Sample Output 17 | 1 -> 2 -> 3 -> 4 -> 5 -> 6 // the head node with value 1 18 | 19 | """ 20 | 21 | # SOLUTION 1 22 | 23 | class LinkedList: 24 | def __init__(self, value): 25 | self.value = value 26 | self.next = None 27 | 28 | # O(n) time | O(1) space where n is the number of nodes in a linked List 29 | def removeDuplicatesFromLinkedList(linkedList): 30 | currentNode = linkedList 31 | while currentNode is not None: 32 | nextDistinctNode = currentNode.next 33 | while nextDistinctNode is not None and nextDistinctNode.value == currentNode.value: 34 | nextDistinctNode = nextDistinctNode.next 35 | 36 | currentNode.next = nextDistinctNode 37 | curretNode = nextDistinctNode 38 | 39 | return linkedList -------------------------------------------------------------------------------- /Remove Kth Node From End.py: -------------------------------------------------------------------------------- 1 | """ 2 | Remove Kth Node From End 3 | """ 4 | 5 | # SOLUTION 1 6 | 7 | # This is an input class. Do not edit. 8 | class LinkedList: 9 | def __init__(self, value): 10 | self.value = value 11 | self.next = None 12 | 13 | 14 | # O(n) time | O(n) space 15 | def removeKthNodeFromEnd(head, k): 16 | nodeList = makeList(head) 17 | length = len(nodeList) 18 | indxToBeRemoved = length - k 19 | indx = 0 20 | currentNode = head 21 | prevNode = None 22 | while currentNode and indx != indxToBeRemoved: 23 | prevNode = currentNode 24 | currentNode = currentNode.next 25 | indx += 1 26 | 27 | if prevNode == None: 28 | head.value = head.next.value 29 | head.next = head.next.next 30 | 31 | elif currentNode: 32 | prevNode.next = currentNode.next 33 | currentNode.next = None 34 | 35 | 36 | def makeList(node): 37 | nodeList = [] 38 | currentNode = node 39 | nodeList.append(currentNode.value) 40 | while currentNode.next is not None: 41 | currentNode = currentNode.next 42 | nodeList.append(currentNode.value) 43 | return nodeList 44 | 45 | # SOLUTION 2 46 | 47 | # This is an input class. Do not edit. 48 | class LinkedList: 49 | def __init__(self, value): 50 | self.value = value 51 | self.next = None 52 | 53 | 54 | def removeKthNodeFromEnd(head, k): 55 | counter = 1 56 | first = head 57 | second = head 58 | while counter <= k: 59 | second = second.next 60 | counter += 1 61 | if second is None: 62 | head.value = head.next.value 63 | head.next = head.next.next 64 | return 65 | while second.next is not None: 66 | second = second.next 67 | first = first.next 68 | first.next = first.next.next 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Reverse Linked List.py: -------------------------------------------------------------------------------- 1 | """ 2 | Reverse Linked List 3 | Write a function that takes in the head of a Singly Linked List, reverses the list in place (i.e., doesn't create a brand new list), and returns its new 4 | head. 5 | Each Linkedlist node has an integer value as well as a next node pointing to the next node in the list or to None/null if it's the tail 6 | of the list 7 | You can assume that the input Linked List will always have at least one node; in other words, the head will never be None / null . 8 | 9 | Sample Input 10 | head = 0 -> 1 -> 2 -> 3 -> 4 -> 5 // the head node with value 0 11 | 12 | Sample Output 13 | 5 -> 4 -> 3 -> 2 -> 1 -> 0 // the new head node with value 5 14 | 15 | """ 16 | # SOLUTION 1 17 | 18 | # This is an input class. Do not edit. 19 | class LinkedList: 20 | def __init__(self, value): 21 | self.value = value 22 | self.next = None 23 | 24 | # O(n) time | 0(1) space - where n is the number of nodes in the Linked List 25 | def reverseLinkedList(head): 26 | previousNode, currentNode = None, head 27 | while currentNode is not None: 28 | nextNode = currentNode.next 29 | currentNode.next = previousNode 30 | previousNode = currentNode 31 | currentNode = nextNode 32 | return previousNode 33 | -------------------------------------------------------------------------------- /Right Sibling Tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Right Sibling Tree o☆ 3 | Write a function that takes in a Binary Tree, transforms it into a Right Sibling Tree, and returns its root. 4 | A Right Sibling Tree is obtained by making every node in a Binary Tree have its right property point to its right sibling 5 | instead of its right child. A node's right sibling is the node immediately 6 | to its right on the same level or None / null if there is no node immediately to its right. 7 | Note that once the transformation is complete, some nodes might no longer have a node pointing to them. For example, in 8 | the sample output below, the node with value 10 no longer has any inbound pointers and is effectively unreachable. 9 | The transformation should be done in place, meaning that the original data structure should be mutated (no new structure 10 | should be created). 11 | Each BinaryTree node has an integer value, a left child node, and a right child node. Children nodes can either be 12 | BinaryTree nodes themselves or None / null. 13 | 14 | Sample Input 15 | 16 | tree = 1 17 | / \ 18 | 2 3 19 | / \ / \ 20 | 4 5 6 7 21 | / \ \ / / \ 22 | 8 9 10 11 12 13 23 | / 24 | 14 25 | 26 | 27 | Sample Output 28 | 1 // the root node with value 1 29 | / 30 | 2----------3 31 | / / 32 | 4-----5-----6-----7 33 | / / / 34 | 8---9 10-11 12-13 // the node with value 10 no longer has a node pointing to it 35 | / 36 | 14 37 | 38 | 39 | """ 40 | 41 | # SOLUTION 1 42 | 43 | # This is the class of the input root. Do not edit it. 44 | class BinaryTree: 45 | def __init__(self, value, left=None, right=None): 46 | self.value = value 47 | self.left = left 48 | self.right = right 49 | 50 | 51 | # O(n) time | 0(d) space - where n is the number of nodes in the Binary Tree and d is the depth (height) of the 52 | # Binary Tree 53 | def rightSiblingTree(root): 54 | mutate(root, None, None) 55 | return root 56 | 57 | def mutate(node, parent, isLeftchild): 58 | if node is None: 59 | return 60 | left, right = node.left, node.right 61 | mutate(left, node, True) 62 | 63 | if parent is None: 64 | node.right = None 65 | elif isLeftchild: 66 | node.right = parent.right 67 | else: 68 | if parent.right is None: 69 | node.right = None 70 | else: 71 | node.right = parent.right.left 72 | mutate(right, node, False) 73 | 74 | -------------------------------------------------------------------------------- /River Sizes.py: -------------------------------------------------------------------------------- 1 | """ 2 | River Sizes 3 | 4 | You're given a two-dimensional array(a matrix) of potentially unequal height and width 5 | containing only 0 s and 1 s.Each 0 represents land, and each 1 represents part of a 6 | river.A river consists of any number of 1 s that are either horizontal or vertically 7 | adjacent (but not diagonal adjacent).The number of adjacent 1 s forming a river 8 | determine its size. 9 | 10 | Note that a river can twist.In other words,it doesn't have to be a straight vertical 11 | line or a straight horizontal line; it can be L-shaped, for example. 12 | 13 | Write a function that returns an array of the sizes of all rivers represented in the 14 | input matrix.The size don't need to be in any particular order. 15 | 16 | Sample input 17 | matrix = [ 18 | [1, 0, 0, 1, 0], 19 | [1, 0, 1, 0, 0], 20 | [0, 0, 1, 0, 1], 21 | [1, 0, 1, 0, 1], 22 | [1, 0, 1, 1, 0], 23 | ] 24 | 25 | Sample Output 26 | [1, 2, 2, 2, 5] // The numbers could be ordered differently 27 | 28 | // The rivers can be clearly seen here: 29 | [ 30 | [1, , , 1, ], 31 | [1, , 1, , ], 32 | [ , , 1, , 1], 33 | [1, , 1, , 1], 34 | [1, , 1, 1, ], 35 | ] 36 | 37 | 38 | """ 39 | # SOLUTION 1 40 | 41 | # O(N) time | O(N) space where N is the total number elements in the matrix. 42 | def riverSizes(matrix): 43 | sizes = [] 44 | visited = [[False for value in row] for row in matrix] 45 | for i in range(len(matrix)): 46 | for j in range(len(matrix[0])): 47 | if visited[i][j]: 48 | continue 49 | traverseNodes(i, j, matrix, visited, sizes) 50 | return sizes 51 | 52 | 53 | def traverseNodes(i, j, matrix, visited, sizes): 54 | currentRiverSize = 0 55 | nodesToExplore = [[i, j]] 56 | while len(nodesToExplore): 57 | currenNode = nodesToExplore.pop() 58 | i = currenNode[0] 59 | j = currenNode[1] 60 | if visited[i][j]: 61 | continue 62 | visited[i][j] = True 63 | if matrix[i][j] == 0: 64 | continue 65 | currentRiverSize += 1 66 | unvisitedNeighbours = getUnvisitedNeighbours(i, j, matrix, visited) 67 | for neighbour in unvisitedNeighbours: 68 | nodesToExplore.append(neighbour) 69 | 70 | if currentRiverSize > 0: 71 | sizes.append(currentRiverSize) 72 | 73 | 74 | def getUnvisitedNeighbours(i, j, matrix, visited): 75 | unvisitedNeighbours = [] 76 | if i > 0 and not visited[i - 1][j]: 77 | unvisitedNeighbours.append([i - 1, j]) 78 | if i < len(matrix) - 1 and not visited[i + 1][j]: 79 | unvisitedNeighbours.append([i + 1, j]) 80 | if j > 0 and not visited[i][j - 1]: 81 | unvisitedNeighbours.append([i, j - 1]) 82 | if j < len(matrix[0]) - 1 and not visited[i][j + 1]: 83 | unvisitedNeighbours.append([i, j + 1]) 84 | 85 | return unvisitedNeighbours 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Run-Length Encoding.py: -------------------------------------------------------------------------------- 1 | """ 2 | Run-Length Encoding 3 | 4 | Write a function that takes in a non-empty string and returns its run-length encoding. 5 | 6 | From Wikipedia, "run-length" encoding is a form of lossless data compression in which 7 | runs of data are stored as a single data value and count, rather than as the original 8 | run."For this problem, a run of data is any sequence of consecutive,identical characters. 9 | So the run "AAA" would be run-length-encoded as "3A". 10 | 11 | To make things more complicated,however,the input string can contain all sorts of 12 | special characters, including numbers.And since encoded data must be decodable,this 13 | means that we can't naively run-length-encode long runs.For example, the run 14 | "AAAAAAAAAAAA" (12 A 's), can't naively be encoded as "12A", since this string can be 15 | decoded as either "AAAAAAAAAAAA" or "1AA".Thus,long runs(runs of 10 or more characters) 16 | should be encoded in a split fashion;the aforementioned run should be encoded as "9A3A" 17 | 18 | Sample Input 19 | string = "AAAAAAAAAAAAABBCCCCDD" 20 | 21 | Sample Output 22 | "9A4A2B4C2D" 23 | 24 | """ 25 | 26 | # SOLUTION 1 27 | 28 | # Running time O(n)| space 0(n) where n is the length of input string 29 | def runLengthEncoding(string): 30 | encodedStringCharacter = [] 31 | currentRunLength = 1 32 | 33 | for i in range(1, len(string)): 34 | currentCharacter = string[i] 35 | previousCharacter = string[i-1] 36 | 37 | if currentCharacter != previousCharacter or currentRunLength == 9: 38 | encodedStringCharacter.append(str(currentRunLength)) 39 | encodedStringCharacter.append(previousCharacter) 40 | currentRunLength = 0 41 | 42 | currentRunLength += 1 43 | 44 | encodedStringCharacter.append(str(currentRunLength)) 45 | encodedStringCharacter.append(string[len(string)-1]) 46 | 47 | return "".join(encodedStringCharacter) 48 | 49 | 50 | # SOLUTION 2 51 | 52 | # Running time O(n)| space 0(n) where n is the length of input string 53 | def runLengthEncoding(string): 54 | encodedStringCharacter = [] 55 | currentRunLength = 0 56 | startCharacter = string[0] 57 | 58 | for i in range(len(string)): 59 | 60 | if string[i] != startCharacter or currentRunLength == 9: 61 | encodedStringCharacter.append(f'{currentRunLength}{startCharacter}') 62 | startCharacter = string[i] 63 | currentRunLength = 0 64 | 65 | currentRunLength += 1 66 | 67 | encodedStringCharacter.append(f'{currentRunLength}{string[i]}') 68 | 69 | return "".join(encodedStringCharacter) 70 | 71 | 72 | -------------------------------------------------------------------------------- /Search in Sorted Matrix.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Search In Sorted Matrix 4 | 5 | You're given a two-dimensional array (a matrix) of distinct integers and a target integer. Each row in the matrix is 6 | sorted, and each column is also sorted; the matrix doesn't necessarily have the same height and width. 7 | 8 | Write a function that returns an array of the row and column indices of the target integer if it's contained in the 9 | matrix, otherwise [-1, -1] . 10 | 11 | Sample Input 12 | matrix = [ 13 | [1, 4, 7, 12, 15, 1000), 14 | [2, 5, 19, 31, 32, 1001], 15 | [3, 8, 24, 33, 35, 1002], 16 | [40, 41, 42, 44, 45, 1003], 17 | [99, 100, 103, 106, 128, 1004], 18 | ] 19 | target = 44 20 | 21 | Sample Output 22 | [3, 3] 23 | 24 | """ 25 | 26 | 27 | # SOLUTION 1 28 | 29 | # O(n + m) time | O(1) space 30 | def searchInSortedMatrix(matrix, target): 31 | row = 0 32 | col = len(matrix[0]) - 1 33 | while row < len(matrix) and col >= 0: 34 | if matrix[row][col] > target: 35 | col -= 1 36 | elif matrix[row][col] < target: 37 | row += 1 38 | else: 39 | return [row, col] 40 | return [-1, -1] 41 | 42 | 43 | # SOLUTION 2 44 | 45 | # O(n * m) time | O(1) space 46 | def searchInSortedMatrix(matrix, target): 47 | for row in range(len(matrix)): 48 | for col in range(len(matrix[row])): 49 | if matrix[row][col] == target: 50 | return [row, col] 51 | else: 52 | return [-1, -1] 53 | -------------------------------------------------------------------------------- /Selection Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Selection Sort 3 | 4 | Write a function that takes in an array of integers and returns a sorted version of that 5 | array.Use the Selection Sort algorithm to sort the array. 6 | 7 | Sample Input 8 | array = [8, 5, 2, 9, 5, 6, 3] 9 | 10 | Sample Output 11 | [2, 3, 5, 5, 6, 8, 9] 12 | """ 13 | 14 | 15 | # SOLUTION 1 16 | 17 | # Running time O(n^2) | space 0(1) 18 | def selectionSort(array): 19 | currentIndx = 0 20 | while currentIndx < len(array) - 1: # Last element is already sorted. 21 | smallestIdx = currentIndx 22 | for i in range(currentIndx + 1, len(array)): 23 | if array[smallestIdx] > array[i]: 24 | smallestIdx = i 25 | 26 | swap(currentIndx, smallestIdx, array) 27 | currentIndx += 1 28 | 29 | return array 30 | 31 | 32 | def swap(i, j, array): 33 | array[i], array[j] = array[j], array[i] 34 | 35 | 36 | 37 | # SOLUTION 2 38 | 39 | # Running time O(n^2) | space 0(1) 40 | def selectionSort(array): 41 | for step in range(len(array) - 1): # Last element is already sorted. 42 | min_idx = step 43 | for i in range(step + 1, len(array)): 44 | if array[i] < array[min_idx]: 45 | min_idx = i 46 | 47 | array[step], array[min_idx] = array[min_idx], array[step] 48 | 49 | return array 50 | 51 | 52 | array = [8, 5, 2, 9, 5, 6, 3] 53 | print(selectionSort(array)) 54 | -------------------------------------------------------------------------------- /Shift Linked List.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shift Linked List ☆ 3 | Write a function that takes in the head of a Singly Linked List and an integer k , shifts the list in place (i.e., doesn't create a brand new 4 | list) by k positions, and returns its new head. 5 | Shifting a Linked List means moving its nodes forward or backward and wrapping them around the list where appropriate. For example, 6 | shifting a Linked List forward by one position would make its tail become the new head of the linked list. 7 | Whether nodes are moved forward or backward is determined by whether k is positive or negative. 8 | Each LinkedList node has an integer value as well as a next node pointing to the next node in the list or to None null if 9 | it's the tail of the list. 10 | You can assume that the input Linked List will always have at least one node; in other words, the head will never be None / null. 11 | Sample Input 12 | head = 0 -> 1 -> 2 -> 3 -> 4 -> 5 // the head node with value ® 13 | k = 2 14 | Sample Output 15 | 4 -> 5 -> -> 1 -> 2 -> 3 // the new head node with value 4 16 | 17 | """ 18 | 19 | # SOLUTION 1 20 | 21 | class LinkedList: 22 | def __init__(self, value): 23 | self.value = value 24 | self.next = None 25 | 26 | # O(n) time | 0(1) space - where n is the number of nodes in the Linked List 27 | def shiftLinkedList(head, k): 28 | listLength = 1 29 | listTail = head 30 | while listTail.next is not None: 31 | listTail = listTail.next 32 | listLength += 1 33 | 34 | offset = abs(k) % listLength 35 | if offset == 0: 36 | return head 37 | newTailPosition = listLength - offset if k > 0 else offset 38 | newTail = head 39 | for i in range(1, newTailPosition): 40 | newTail = newTail.next 41 | 42 | newHead = newTail.next 43 | newTail.next = None 44 | listTail.next = head 45 | return newHead 46 | -------------------------------------------------------------------------------- /Shifted Binary Search.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shifted Binary Search ☆ 3 | Write a function that takes in a sorted array of distinct integers as well as a target integer. The caveat is that the integers in the array 4 | have been shifted by some amount in other words, they've been moved to the left or to the right by one or more positions. For example, 5 | [1, 2, 3, 4] might have turned into [3, 4, 1, 2] . 6 | The function should use a variation of the Binary Search algorithm to determine if the target integer is contained in the array and should 7 | return its index if it is, otherwise -1 . 8 | If you're unfamiliar with Binary Search, we recommend watching the Conceptual Overview section of the Binary Search question's video 9 | explanation before starting to code. 10 | 11 | Sample Input 12 | array = [45, 61, 71, 72, 73, 0, 1, 21, 33, 37] 13 | target = 33 14 | Sample Output 15 | 8 16 | """ 17 | 18 | # SOLUTION 1 19 | 20 | # O(log(n)) time | O(log(n)) space 21 | def shiftedBinarySearch(array, target): 22 | return shiftedBinarySearchHelper (array, target, 0, len(array) - 1) 23 | 24 | def shiftedBinarySearchHelper (array, target, left, right): 25 | if left > right: 26 | return -1 27 | middle = (left + right) // 2 28 | potentialMatch = array[middle] 29 | leftNum = array[left] 30 | rightNum = array[right] 31 | if target == potentialMatch: 32 | return middle 33 | elif leftNum <= potentialMatch: 34 | if potentialMatch > target >= leftNum: 35 | return shiftedBinarySearchHelper(array, target, left, middle - 1) 36 | else: 37 | return shiftedBinarySearchHelper(array, target, middle + 1, right) 38 | else: 39 | if potentialMatch < target <= rightNum: 40 | return shiftedBinarySearchHelper(array, target, middle + 1, right) 41 | else: 42 | return shiftedBinarySearchHelper(array, target, left, middle - 1) 43 | 44 | 45 | # SOLUTION 2 46 | 47 | # O(log(n)) time | 0(1) space 48 | def shiftedBinarySearch(array, target): 49 | return shiftedBinarySearchHelper (array, target, 0, len(array) - 1) 50 | 51 | def shiftedBinarySearchHelper(array, target, left, right): 52 | while left <= right: 53 | middle = (left + right) // 2 54 | potentialMatch = array[middle] 55 | leftNum = array[left] 56 | rightNum = array[right] 57 | if target == potentialMatch: 58 | return middle 59 | elif leftNum <= potentialMatch: 60 | if potentialMatch > target >= leftNum: 61 | right = middle - 1 62 | else: 63 | left = middle + 1 64 | else: 65 | if potentialMatch < target <= rightNum: 66 | left = middle + 1 67 | else: 68 | right = middle - 1 69 | return -1 70 | -------------------------------------------------------------------------------- /Shorten Path.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shorten Path 3 | Write a function that takes in a non-empty string representing a valid Unix-shell path and returns a shortened version of that path. 4 | A path is a notation that represents the location of a file or directory in a file system. 5 | A path can be an absolute path, meaning that it starts at the root directory in a file system, or a relative path, meaning that it starts at the 6 | current directory in a file system. 7 | In a Unix-like operating system, a path is bound by the following rules: 8 | 9 | • The root directory is represented by a / . This means that if a path starts with / it's an absolute path; if it doesn't, it's a relative 10 | path. 11 | 12 | • The symbol, otherwise represents the directory separator. This means that the path /foo/bar is the location of the directory 13 | bar inside the directory foo , which is itself located inside the root directory. 14 | 15 | • The symbol .. represents the parent directory. This means that accessing files or directories in /foo/bar/.. is equivalent to 16 | accessing files or directories in /foo. 17 | 18 | • The symbol . represents the current directory. This means that accessing files or directories in /foo/bar/. is equivalent to 19 | accessing files or directories in /foo/bar. 20 | 21 | • The symbols / and . can be repeated sequentially without consequence; the symbol .. cannot, however, because repeating 22 | it sequentially means going further up in parent directories. For example. /foo/bar/baz/././. and /foo/bar/baz are 23 | equivalent paths, but /foo/bar/baz/../../../ and /foo/bar/baz definitely aren't. The only exception is with the root 24 | directory: /../../.. and / are equivalent, because the root directory has no parent directory, which means that repeatedly 25 | accessing parent directories does nothing. 26 | Note that the shortened version of the path must be equivalent to the original path. In other words, it must point to the same file or 27 | directory as the original path. 28 | 29 | Sample Input 30 | path = "/foo/../test/../test/../foo//bar/./baz" 31 | Sample Output 32 | "/foo/bar/baz" // This path is equivalent to the input path. 33 | 34 | """ 35 | 36 | # SOLUTION 1 37 | 38 | # O(n) time | O(n) space - where n is the length of the path 39 | def shortenPath(path): 40 | startsWithSlash = path[0] == "/" 41 | tokens = filter(isImportantToken, path.split("/")) 42 | stack = [] 43 | if startsWithSlash: 44 | stack.append("") 45 | for token in tokens: 46 | if token == "..": 47 | if len(stack) == 0 or stack[-1] == "..": 48 | stack.append(token) 49 | elif stack[-1] != "": 50 | stack.pop() 51 | else: 52 | stack.append(token) 53 | 54 | if len(stack) == 1 and stack[0] == "": 55 | return "/" 56 | return "/".join(stack) 57 | 58 | def isImportantToken(token): 59 | return len(token) > 0 and token != "." 60 | -------------------------------------------------------------------------------- /Single Cycle Check.py: -------------------------------------------------------------------------------- 1 | """ 2 | Single Cycle Check 3 | 4 | You're given an array of integers where each integer represents a jump of its value in the array. 5 | For instance ,the integer 2 represents a jump of two indices forward in the array; the integer -3 6 | represents a jump of three indices backward in the array. 7 | 8 | If a jump spills past the array's bounds,it wraps over to the other side.For instance , a jump of 9 | -1 at index 0 brings us to the last index in the array.Similarly, a jump of 1 at the last index in 10 | the array brings us to index 0. 11 | 12 | Write a function that returns a boolean representing whether the jumps in the array form a single 13 | cycle.A single cycle occurs if, starting at any index in the array and following the jumps,every 14 | element in the array is visited exactly once before landing back on the starting index. 15 | 16 | Sample Input 17 | array = [2, 3, 1, -4, -4, 2] 18 | 19 | Sample Output 20 | true 21 | """ 22 | 23 | # SOLUTION 1 24 | 25 | # O(n) time | O(1) space 26 | def hasSingleCycle(array): 27 | numElementsVisited = 0 28 | currentIdx = 0 29 | while numElementsVisited < len(array): 30 | if numElementsVisited > 0 and currentIdx == 0: 31 | return False 32 | numElementsVisited += 1 33 | currentIdx = getNextIdx(currentIdx, array) 34 | return currentIdx == 0 35 | 36 | 37 | def getNextIdx(currentIdx, array): 38 | jump = array[currentIdx] 39 | nextIdx = (currentIdx + jump) % len(array) 40 | return nextIdx if nextIdx >= 0 else nextIdx + len(array) 41 | -------------------------------------------------------------------------------- /Smallest Difference.py: -------------------------------------------------------------------------------- 1 | """ 2 | Smallest Difference 3 | 4 | Write a function that takes in two non-empty arrays of integers,finds the pair of numbers 5 | (one from each array) whose absolute difference is closest to zero,and returns an array 6 | containing these two numbers,with the number from the first array in the first position. 7 | 8 | Note that the absolute difference of two integers is the distance between them on the 9 | real number line.For example,the absolute difference of -5 and 5 is 10, and the absolute 10 | difference of -5 and -4 is 1. 11 | 12 | You can assume that there will only be one pair of numbers with the smallest. 13 | 14 | Sample Input 15 | arrayOne = [-1, 5, 10 ,20, 28, 3] 16 | arrayTwo = [26, 134, 135, 15, 17] 17 | 18 | Sample Output 19 | [28,26] 20 | 21 | """ 22 | # SOLUTION 1 23 | 24 | # time O(n^2) | space O(1) 25 | def smallestDifference(arrayOne, arrayTwo): 26 | smallestFirst = arrayOne[0] 27 | smallestSecond = arrayTwo[0] 28 | current_diff = abs(arrayOne[0] - arrayTwo[0]) 29 | 30 | for i in range(len(arrayOne)): 31 | for j in range(len(arrayTwo)): 32 | if abs(arrayOne[i] - arrayTwo[j]) < current_diff: 33 | current_diff = abs(arrayOne[i] - arrayTwo[j]) 34 | smallestFirst = arrayOne[i] 35 | smallestSecond = arrayTwo[j] 36 | return [smallestFirst, smallestSecond] 37 | 38 | 39 | # SOLUTION 2 40 | 41 | # time O(nlog(n) + O(mlog(m)) | space O(1) 42 | def smallestDifference(arrayOne, arrayTwo): 43 | arrayOne.sort() 44 | arrayTwo.sort() 45 | idxOne = 0 46 | idxTwo = 0 47 | smallest = float("inf") 48 | current = float("inf") 49 | smallestPair = [] 50 | while idxOne < len(arrayOne) and idxTwo < len(arrayTwo): 51 | firstNum = arrayOne[idxOne] 52 | secondNum = arrayTwo[idxTwo] 53 | if firstNum < secondNum: 54 | current = secondNum - firstNum 55 | idxOne += 1 56 | elif secondNum < firstNum: 57 | current = firstNum - secondNum 58 | idxTwo += 1 59 | else: 60 | return [firstNum, secondNum] 61 | if smallest > current: 62 | smallest = current 63 | smallestPair = [firstNum, secondNum] 64 | return smallestPair 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Sort Stack.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sort Stack 3 | Write a function that takes in an array of integers representing a stack, recursively sorts the stack in place (i.e., 4 | doesn't create a brand new array), and returns it. 5 | 6 | The array must be treated as a stack, with the end of the array as the top of the stack. Therefore, you're only allowed 7 | to 8 | • Pop elements from the top of the stack by removing elements from the end of the array using the built-in .pop() method 9 | in your programming language of choice. 10 | • Push elements to the top of the stack by appending elements to the end of the array using the built-in .append() 11 | method in your programming language of choice. 12 | • Peek at the element on top of the stack by accessing the last element in the array. 13 | 14 | You're not allowed to perform any other operations on the input array, including accessing elements (except for the last 15 | element), moving elements, etc.. You're also not allowed to use any other data structures, and your 16 | solution must be recursive. 17 | 18 | 19 | Sample Input 20 | stack = [-5, 2, -2, 4, 3, 1] 21 | 22 | Sample Output 23 | [-5, -2, 1, 2, 3, 4] 24 | 25 | """ 26 | # SOLUTION 1 27 | 28 | # O(n^2) time | O(n) space - where n is the length of the stack 29 | def sortStack(stack): 30 | if len(stack) == 0: 31 | return stack 32 | top = stack.pop() 33 | sortStack(stack) 34 | insertInSortedOrder(stack, top) 35 | return stack 36 | 37 | def insertInSortedOrder(stack, value): 38 | if len(stack) == 0 or stack[len(stack) - 1] <= value: 39 | stack.append(value) 40 | return 41 | top = stack.pop() 42 | insertInSortedOrder(stack, value) 43 | stack.append(top) 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Sorted Square Array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sorted Square Array 3 | 4 | Write a function that takes in non-empty array of integers that are sorted 5 | in ascending order and returns a new array of the same length with the squares 6 | of the original integers also sorted in ascending order. 7 | 8 | Sample Input 9 | array = [1 ,2 ,3, 5, 6, 8, 9] 10 | 11 | Sample Output 12 | [1 , 4, 9, 25, 36, 64, 81] 13 | """ 14 | # SOLUTION 1 15 | 16 | # O(nlogn) time | O(n) space - where n is the length of the input array. 17 | def sortedSquaredArray(array): 18 | sortedSquares = [0 for _ in array] 19 | 20 | for idx in range(len(array)): 21 | value = array[idx] 22 | sortedSquares[idx] = value * value 23 | 24 | sortedSquares.sort() 25 | return sortedSquares 26 | 27 | # SOLUTION 2 28 | 29 | # O(n) time | O(n) space - where n is the length of the input array. 30 | def sortedSquaredArray(array): 31 | sortedSquares = [0 for _ in array] 32 | smallerValueIdx = 0 33 | largerValueIdx = len(array) - 1 34 | 35 | for idx in reversed(range(len(array))): 36 | smallerValue = array[smallerValueIdx] 37 | largerValue = array[largerValueIdx] 38 | 39 | if abs(smallerValue) > abs(largerValue): 40 | sortedSquares[idx] = smallerValue * smallerValue 41 | smallerValueIdx += 1 42 | 43 | else: 44 | sortedSquares[idx] = largerValue * largerValue 45 | largerValueIdx -= 1 46 | 47 | return sortedSquares 48 | 49 | 50 | -------------------------------------------------------------------------------- /Spiral Traverse.py: -------------------------------------------------------------------------------- 1 | """ 2 | Spiral Traverse 3 | 4 | Write a function that takes in an xm two-dimensional array(that can be square-shaped 5 | when n==m) and returns a one-dimensional array of all array's elements in spiral order, 6 | 7 | Spiral order starts at the top left corner of the two-dimensional array,goes to the 8 | right and proceeds in a spiral pattern all the way until every element has been visited. 9 | 10 | Sample Input 11 | array = [ 12 | [1, 2, 3, 4] 13 | [12, 13, 14, 5], 14 | [11, 16, 15, 6], 15 | [10, 9, 8, 7], 16 | ] 17 | 18 | Sample Output 19 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] 20 | 21 | """ 22 | # SOLUTION 1 23 | 24 | # Iterative solution 25 | # time O(N) | space O(N) - where n is the total number of elements in the array 26 | def spiralTraverse(array): 27 | result = [] 28 | startrow, endrow = 0, len(array) - 1 29 | startcol, endcol = 0, len(array[0]) - 1 30 | 31 | while (startrow <= endrow) and (startcol <= endcol): 32 | for col in range(startcol, endcol + 1): 33 | result.append(array[startrow][col]) 34 | 35 | for row in range(startrow + 1, endrow + 1): 36 | result.append(array[row][endcol]) 37 | 38 | for col in reversed(range(startcol, endcol)): 39 | if startrow == endrow: 40 | break 41 | result.append(array[endrow][col]) 42 | 43 | for row in reversed(range(startrow + 1, endrow)): 44 | if startcol == endcol: 45 | break 46 | result.append(array[row][startcol]) 47 | 48 | startrow += 1 49 | endrow -= 1 50 | startcol += 1 51 | endcol -= 1 52 | 53 | return result 54 | 55 | 56 | # SOLUTION 2 57 | 58 | # Recursive solution 59 | # time O(N) | space O(N) - where n is the total number of elements in the array 60 | def spiralTraverse(array): 61 | result = [] 62 | spiral(array, 0, len(array) - 1, 0, len(array[0]) - 1, result) 63 | return result 64 | 65 | 66 | def spiral(array, startrow, endrow, startcol, endcol, result): 67 | if startrow > endrow or startcol > endcol: 68 | return 69 | 70 | for col in range(startcol, endcol + 1): 71 | result.append(array[startrow][col]) 72 | 73 | for row in range(startrow + 1, endrow + 1): 74 | result.append(array[row][endcol]) 75 | 76 | for col in reversed(range(startcol, endcol)): 77 | if startrow == endrow: 78 | break 79 | result.append(array[endrow][col]) 80 | 81 | for row in reversed(range(startrow + 1, endrow)): 82 | if startcol == endcol: 83 | break 84 | result.append(array[row][startcol]) 85 | 86 | spiral(array, startrow + 1, endrow - 1, startcol + 1, endcol - 1, result) 87 | -------------------------------------------------------------------------------- /Staircase Traversal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Staircase Traversal 3 | 4 | You're given two positive integers representing the height of a staircase and the maximum 5 | number of steps that you can advance up the staircase at a time.Write a function that 6 | returns the number of ways in which you can climb the staircase. 7 | 8 | For example,if you were given a staircase of height = 3 and maxSteps = 2 you could climb the 9 | staircase in 3 ways.You could take 1 step, 1step, then 1 step, you could also take 1 step, then 10 | 2 steps, and you could take 2 steps,then 1 step. 11 | 12 | Note that maxSteps <= height will always be true. 13 | 14 | Sample Input 15 | height = 4 16 | maxSteps = 2 17 | 18 | Sample Output 19 | 5 20 | // You can climb the staircase in the following ways: 21 | // 1, 1, 1, 1 22 | // 1, 1, 2 23 | // 1, 2, 1 24 | // 2, 1, 1 25 | // 2, 2 26 | """ 27 | 28 | # SOLUTION 1 29 | 30 | # O(K^n) time | O(n) space where n is the height of the staircase and k is the number of staircase available 31 | def staircaseTraversal(height, maxSteps): 32 | return numberOfWaysToTop(height, maxSteps) 33 | 34 | 35 | def numberOfWaysToTop(height, maxSteps): 36 | if height <= 1: 37 | return 1 38 | numberOfWays = 0 39 | for step in range(1, min(maxSteps, height) + 1): 40 | numberOfWays += numberOfWaysToTop(height - step, maxSteps) 41 | 42 | return numberOfWays 43 | 44 | 45 | # SOLUTION 2 46 | 47 | # O(K*n) time | O(n) space where n is the height of the staircase and k is the number of staircase available 48 | def staircaseTraversal(height, maxSteps): 49 | return numberOfWaysToTop(height, maxSteps, {0: 1, 1: 1}) 50 | 51 | 52 | def numberOfWaysToTop(height, maxSteps, memoize): 53 | if height in memoize: 54 | return memoize[height] 55 | numberOfWays = 0 56 | for step in range(1, min(maxSteps, height) + 1): 57 | numberOfWays += numberOfWaysToTop(height - step, maxSteps, memoize) 58 | 59 | memoize[height] = numberOfWays 60 | 61 | return numberOfWays 62 | 63 | # SOLUTION 3 64 | 65 | # O(K*n) time | O(n) space where n is the height of the staircase and k is the number of staircase available 66 | def staircaseTraversal(height, maxSteps): 67 | waysToTop = [0 for _ in range(height + 1)] 68 | waysToTop[0] = 1 69 | waysToTop[1] = 1 70 | 71 | for currentHeight in range(2, height + 1): 72 | step = 1 73 | while step <= maxSteps and step <= currentHeight: 74 | waysToTop[currentHeight] = waysToTop[currentHeight] + waysToTop[currentHeight - step] 75 | step += 1 76 | 77 | return waysToTop[height] 78 | -------------------------------------------------------------------------------- /Subarray Sort.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Subarray Sort 4 | 5 | Write a function that takes in an array of at least two integers and that returns an array of the starting 6 | and ending indices of the smallest subarray in the input array that needs to be sorted in place in order for 7 | the entire input array to be sorted (in ascending order). 8 | If the input array is already sorted, the function should return [-1, -1] . 9 | 10 | Sample Input 11 | array = [1, 2, 4, 7, 10, 11, 7, 12, 6, 7, 16, 18, 19] 12 | 13 | Sample Output 14 | [3, 9] 15 | """ 16 | # SOLUTION 1 17 | 18 | # O(n) time | O(1) space 19 | def subarraySort(array): 20 | minOutOfOrder = float("inf") 21 | maxOutOfOrder = float("-inf") 22 | for i in range(len(array)): 23 | num = array[i] 24 | if isOutOfOrder(i, num, array): 25 | minOutOfOrder = min(minOutOfOrder, num) 26 | maxOutOfOrder = max(maxOutOfOrder, num) 27 | if minOutOfOrder == float("inf"): 28 | return [-1, -1] 29 | subarrayLeftIdx = 0 30 | while minOutOfOrder >= array[subarrayLeftIdx]: 31 | subarrayLeftIdx += 1 32 | subarrayRightIdx = len(array) - 1 33 | while maxOutOfOrder <= array[subarrayRightIdx]: 34 | subarrayRightIdx -= 1 35 | return [subarrayLeftIdx, subarrayRightIdx] 36 | 37 | def isOutOfOrder(i, num, array): 38 | if i == 0: 39 | return num > array[i + 1] 40 | if i == len(array) - 1: 41 | return num < array[i - 1] 42 | return num > array[i + 1] or num < array[i - 1] 43 | 44 | -------------------------------------------------------------------------------- /Suffix Trie Construction.py: -------------------------------------------------------------------------------- 1 | """ 2 | Suffix Trie Construction 3 | 4 | Write a SuffixTrie class for a Suffix-Trie-like data structure. The class should have a root property set to be the 5 | root node of the trie and should support: 6 | • Creating the trie from a string this will be done by calling the populateSuffixTrieFrom method upon class 7 | instantiation, which should populate the root of the class. 8 | • Searching for strings in the trie. 9 | 10 | Note that every string added to the trie should end with the special endSymbol character: "*" 11 | If you're unfamiliar with Suffix Tries, we recommend watching the Conceptual Overview section of this question's 12 | video explanation before starting to code. 13 | 14 | Sample Input (for creation) 15 | string = "babc" 16 | 17 | Sample Output (for creation) 18 | The structure below is the root of the trie. 19 | { 20 | "C": {"*": true}, 21 | "b": { 22 | "c": {"*": true}, 23 | "a": {"b": {"c": {"*": true}}}, 24 | }, 25 | "a": {"b": {"c": {"*": true}}}, 26 | } 27 | 28 | Sample Input (for searching in the suffix trie above) 29 | string = "abc" 30 | 31 | Sample Output (for searching in the suffix trie above) 32 | true 33 | 34 | """ 35 | # SOLUTION 1 36 | 37 | # Do not edit the class below except for the 38 | # populateSuffixTrieFrom and contains methods. 39 | # Feel free to add new properties and methods 40 | # to the class. 41 | class SuffixTrie: 42 | def __init__(self, string): 43 | self.root = {} 44 | self.endSymbol = "*" 45 | self.populateSuffixTrieFrom(string) 46 | 47 | # O(n^2) time | O(n^2) space 48 | def populateSuffixTrieFrom(self, string): 49 | for i in range(len(string)): 50 | self.insertSubstringStartingAt(i,string) 51 | 52 | def insertSubstringStartingAt(self, i, string): 53 | node = self.root 54 | for j in range(i, len(string)): 55 | letter = string[j] 56 | if letter not in node: 57 | node[letter] = {} 58 | node = node[letter] 59 | node[self.endSymbol] = True 60 | 61 | # O(m) time | O(1) space 62 | def contains(self, string): 63 | node = self.root 64 | for letter in string: 65 | if letter not in node: 66 | return False 67 | node = node[letter] 68 | return self.endSymbol in node 69 | 70 | -------------------------------------------------------------------------------- /Sunset Views.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sunset Views 3 | 4 | Given an array of buildings and a direction that all of the buildings face,return an 5 | array of the indices of the buildings that can see the sunset. 6 | 7 | A building can see the sunset if it's strictly taller than all of the buildings that 8 | come after it in the direction that it faces. 9 | 10 | The input array named buildings contains positive, non-zero integers representing the 11 | heights of the buildings. A building at index i thus has a height denote by buildings[i]. 12 | All of the buildings face the same direction , and this direction is either east or west, 13 | denoted by the input string named direction, which will always be equal to either "EAST" 14 | OR "WEST".In relation to the input array,you can interpret these directions as right for 15 | east and left for west. 16 | 17 | Important note: the indices in the output array should be sorted in ascending order. 18 | 19 | Sample Input #1 20 | buildings = [3, 5, 4, 4, 3, 1, 3, 2] 21 | direction = "EAST" 22 | 23 | Sample Output #1 24 | [1, 3, 6, 7] 25 | 26 | // Below is a visual representation of the sample input. 27 | // _ 28 | // | |_ _ 29 | // _| | | |_ _ 30 | // | | | | | | | |_ 31 | // | | | | | |_| | | 32 | // |_|_|_|_|_|_|_|_| 33 | 34 | 35 | Sample Input #2 36 | buildings = [3, 5, 4, 4, 3, 1, 3, 2] 37 | direction = "WEST" 38 | 39 | Sample Output #2 40 | [0, 1] 41 | 42 | """ 43 | # SOLUTION 1 44 | 45 | # O(n) time | O(n) space 46 | def sunsetViews(buildings, direction): 47 | buildingsWithSunsetViews = [] 48 | 49 | startIdx = 0 if direction == "WEST" else len(buildings) - 1 50 | step = 1 if direction == "WEST" else -1 51 | 52 | idx = startIdx 53 | runningMaxHeight = 0 54 | while 0 <= idx < len(buildings): 55 | buildingHeight = buildings[idx] 56 | 57 | if buildingHeight > runningMaxHeight: 58 | buildingsWithSunsetViews.append(idx) 59 | 60 | runningMaxHeight = max(runningMaxHeight, buildingHeight) 61 | 62 | idx += step 63 | 64 | if direction == "EAST": 65 | return buildingsWithSunsetViews[::-1] 66 | 67 | return buildingsWithSunsetViews 68 | 69 | # SOLUTION 2 70 | 71 | # O(n) time | O(n) space 72 | def sunsetViews(buildings, direction): 73 | candidateBuildings = [] 74 | 75 | startIdx = 0 if direction == "EAST" else len(buildings) - 1 76 | step = 1 if direction == "EAST" else -1 77 | 78 | idx = startIdx 79 | while 0 <= idx < len(buildings): 80 | buildingHeight = buildings[idx] 81 | 82 | while len(candidateBuildings) > 0 and buildings[candidateBuildings[-1]] <= buildingHeight: 83 | candidateBuildings.pop() 84 | 85 | candidateBuildings.append(idx) 86 | 87 | idx += step 88 | 89 | if direction == "WEST": 90 | return candidateBuildings[::-1] 91 | 92 | return candidateBuildings 93 | -------------------------------------------------------------------------------- /Tandem Bicycle.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tandem Bicycle 3 | A tandem bicycle is a bicycle that's operated by two people: person A and person B. Both people pedal the bicycle, 4 | but the person that pedals faster dictates the speed of the bicycle. So if person A pedals at a speed of 5, and person 5 | B pedals at a speed of 4, the tandem bicycle moves at a speed of 5 (i.e., tandemSpeed = max(speedA, speedB) ). 6 | 7 | You're given two lists of positive integers: one that contains the speeds of riders wearing red shirts and one that 8 | contains the speeds of riders wearing blue shirts. Each rider is represented by a single positive integer, which is 9 | the speed that they pedal a tandem bicycle at. Both lists have the same length, meaning that there are as many red-shirt 10 | riders as there are blue-shirt riders. Your goal is to pair every rider wearing a red shirt with a rider wearing a 11 | blue shirt to operate a tandem bicycle. 12 | 13 | Write a function that returns the maximum possible total speed or the minimum possible total speed of all of the 14 | tandem bicycles being ridden based on an input parameter, fastest . If fastest = true , your function should return 15 | the maximum possible total speed; otherwise it should return the minimum total speed."Total speed" is defined as the 16 | sum of the speeds of all the tandem bicycles being ridden. For example, if there are 4 riders (2 red-shirt riders and 17 | 2 blue-shirt riders) who have speeds of 1, 3, 4, 5, and if they're paired on tandem bicycles as follows: [1, 4], [5, 3] , 18 | then the total speed of these tandem bicycles is 4 + 5 = 9. 19 | 20 | Sample Input 21 | redshirtSpeeds = [5, 5, 3, 9, 2] 22 | blueShirtSpeeds = [3, 6, 7, 2, 1] 23 | fastest = true 24 | 25 | Sample Output 26 | 32 27 | """ 28 | 29 | # SOLUTION 1 30 | 31 | # O(nlog(n)) time | O(1) space 32 | def tandemBicycle(redShirtSpeeds, blueShirtSpeeds, fastest): 33 | sums = 0 34 | if fastest: 35 | sorted_red = sorted(redShirtSpeeds) 36 | sorted_blue = sorted(blueShirtSpeeds, reverse=True) 37 | elif not fastest: 38 | sorted_red = sorted(redShirtSpeeds) 39 | sorted_blue = sorted(blueShirtSpeeds) 40 | 41 | for i in range(len(sorted_red)): 42 | sums += max(sorted_red[i], sorted_blue[i]) 43 | 44 | return sums 45 | 46 | 47 | # SOLUTION 2 48 | 49 | # O(nlog(n)) time | O(1) space 50 | def tandemBicycle(redShirtSpeeds, blueShirtSpeeds, fastest): 51 | redShirtSpeeds.sort() 52 | blueShirtSpeeds.sort() 53 | 54 | if fastest: 55 | reverseArrayInPlace(redShirtSpeeds) 56 | 57 | totalSpeed = 0 58 | for idx in range(len(redShirtSpeeds)): 59 | rider1 = redShirtSpeeds[idx] 60 | rider2 = blueShirtSpeeds[idx] 61 | 62 | totalSpeed += max(rider1, rider2) 63 | return totalSpeed 64 | 65 | 66 | def reverseArrayInPlace(array): 67 | start = 0 68 | end = len(array) - 1 69 | while start < end: 70 | array[start], array[end] = array[end], array[start] 71 | start += 1 72 | end -= 1 73 | -------------------------------------------------------------------------------- /Three Number Sort.py: -------------------------------------------------------------------------------- 1 | """ 2 | Three Number Sort 3 | 4 | You're given an array of integers and another array of three distinct 5 | integers.The first array is guaranteed to only contain integers that are in 6 | the second array,and the second array represents a desired order for the 7 | integers in the first array.For example, a second array of [x, y, z] represents 8 | a desired order of [x, x, ..., x, y, y, ..., y, z, z, ..., z] in the first array. 9 | 10 | Write a function that sorts the first array according to the desired order 11 | in the second array. 12 | 13 | The function should perform this in place (i.e it should mutate the input array), 14 | and that the first array won't necessarily contain all three integers found in the 15 | second array-it might only contain one or two. 16 | 17 | Sample Input 18 | array = [1, 0, 0, -1, -1, 0, 1, 1] 19 | order = [0, 1, -1] 20 | 21 | Sample Output 22 | [0, 0, 0, 1, 1, 1, -1, -1] 23 | 24 | """ 25 | # SOLUTION 1 26 | 27 | # time O(n) | space O(1) where n is the length of array. 28 | def threeNumberSort(array, order): 29 | position = 0 30 | for i in range(len(order)): 31 | for j in range(len(array)): 32 | if order[i] == array[j]: 33 | array[position], array[j] = array[j], array[position] 34 | position += 1 35 | 36 | return array 37 | 38 | 39 | -------------------------------------------------------------------------------- /Three Number Sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | THREE NUMBER SUM 3 | 4 | Write a function that takes in a non-empty array of distinct integers and an integer 5 | representing a target sum.The function should find all triplets in the array that sum 6 | up to the target sum and return a two-dimensional array of all these triplets.The 7 | numbers in each triplet should be ordered in ascending order,and the triplets 8 | themselves should be ordered in ascending order with respect to the numbers they hold. 9 | 10 | If no three numbers sum up to the target sum, the function should return an empty array. 11 | 12 | Sample Input 13 | array = [12, 3, 1, 2, -6, 5, -8, 6] 14 | target = 0 15 | 16 | Sample Output 17 | [[-8,2,6], [-8,3,5], [-6,1,5]] 18 | 19 | """ 20 | # SOLUTION 1 21 | 22 | # time O(n^3) | space O(1) 23 | def threeNumberSum(array, targetSum): 24 | array.sort() 25 | 26 | triplets = [] 27 | for i in range(len(array) - 2): 28 | first_no = array[i] 29 | for j in range(i + 1, len(array) - 1): 30 | second_no = array[j] 31 | for k in range(j + 1, len(array)): 32 | third_no = array[k] 33 | if first_no + second_no + third_no == targetSum: 34 | triplets.append([first_no, second_no, third_no]) 35 | 36 | return triplets 37 | 38 | 39 | # SOLUTION 2 40 | 41 | # time O(n^2) | space O(n) 42 | def threeNumberSum(array, targetSum): 43 | array.sort() 44 | triplets = [] 45 | for i in range(len(array) - 2): 46 | left = i + 1 47 | right = len(array) - 1 48 | while left < right: 49 | if array[i] + array[left] + array[right] == targetSum: 50 | triplets.append([array[i], array[left], array[right]]) 51 | left += 1 52 | right -= 1 53 | elif array[i] + array[left] + array[right] < targetSum: 54 | left += 1 55 | elif array[i] + array[left] + array[right] > targetSum: 56 | right -= 1 57 | return triplets 58 | -------------------------------------------------------------------------------- /Two Number Sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | Two Number Sum 3 | Write a function that takes in a non-empty array of distinct integers and an integer 4 | representing a target sum.If no any two numbers in the input array sum up to the 5 | target sum,the function should return an empty array. 6 | 7 | Note that the target sum has to be obtained by summing two different integers 8 | in the array; you can't add a single integer to itself in order to obtain the 9 | target sum. 10 | 11 | You can assume that there will be at most one pair of numbers summing up to 12 | the target sum 13 | 14 | Sample input 15 | array = [3, 5,-4, 8, 11, 1, -1, 6] 16 | targetSum = 10 17 | 18 | Sample Output 19 | [-1, 11] 20 | 21 | """ 22 | 23 | # SOLUTION 1 24 | 25 | # Time O(n^2) | Space O(1) 26 | def twoNumberSum(array, targetSum): 27 | for i in range(len(array) - 1): 28 | first_no = array[i] 29 | for j in range(i + 1, len(array)): 30 | second_no = array[j] 31 | if first_no + second_no == targetSum: 32 | return [first_no, second_no] 33 | return [] 34 | 35 | 36 | # SOLUTION 2 37 | 38 | # Time O(n) | Space O(n) 39 | def twoNumberSum(array, targetSum): 40 | # time O(n) | space O(n) 41 | nums = {} 42 | for num in array: 43 | potentialNum = targetSum - num 44 | if potentialNum in nums: 45 | return [potentialNum, num] 46 | else: 47 | nums[num] = True 48 | return [] 49 | 50 | 51 | # SOLUTION 3 52 | 53 | # Time O(nlog(n)) | Space O(1) 54 | def twoNumberSum(array, targetSum): 55 | array.sort() 56 | left = 0 57 | right = len(array) - 1 58 | while left < right: 59 | if array[left] + array[right] == targetSum: 60 | return [array[left], array[right]] 61 | 62 | elif array[left] + array[right] < targetSum: 63 | left += 1 64 | 65 | else: 66 | right -= 1 67 | return [] 68 | -------------------------------------------------------------------------------- /Underscorify Substring.py: -------------------------------------------------------------------------------- 1 | """ 2 | Underscorify Substring ☆ 3 | Write a function that takes in two strings: a main string and a potential substring of the main string. The function should return a version 4 | of the main string with every instance of the substring in it wrapped between underscores. 5 | If two or more instances of the substring in the main string overlap each other or sit side by side, the underscores relevant to these 6 | substrings should only appear on the far left of the leftmost substring and on the far right of the rightmost substring. If the main string 7 | doesn't contain the other string at all, the function should return the main string intact. 8 | 9 | Sample Input 10 | string = "testthis is a testtest to see if testestest it works" 11 | substring = "test" 12 | Sample Output 13 | "_test_this is a _testtest_ to see if _testestest_ it works" 14 | 15 | """ 16 | 17 | # SOLUTION 1 18 | 19 | # Average case: 0(n + m) | O(n) space - where n is the length 20 | # of the main string and n is the length of the substring 21 | def underscorifySubstring(string, substring): 22 | locations = collapse(getLocations (string, substring)) 23 | return underscorify(string, locations) 24 | 25 | def getLocations(string, substring): 26 | locations = [] 27 | startIdx = 0 28 | while startIdx < len(string): 29 | nextIdx = string.find(substring, startIdx) 30 | if nextIdx != -1: 31 | locations.append( [nextIdx, nextIdx + len(substring)]) 32 | startIdx = nextIdx + 1 33 | else: 34 | break 35 | return locations 36 | 37 | def collapse(locations): 38 | if not len(locations): 39 | return locations 40 | newLocations = [locations[0]] 41 | previous = newLocations[0] 42 | for i in range(1, len(locations)): 43 | current = locations[i] 44 | if current[0] <= previous[1]: 45 | previous[1] = current[1] 46 | else: 47 | newLocations.append(current) 48 | previous = current 49 | return newLocations 50 | 51 | def underscorify(string, locations): 52 | locationsIdx = 0 53 | stringIdx = 0 54 | inBetweenUnderscores = False 55 | finalChars = [] 56 | i = 0 57 | while stringIdx < len(string) and locationsIdx < len(locations): 58 | if stringIdx == locations[locationsIdx][i]: 59 | finalChars.append("_") 60 | inBetweenUnderscores = not inBetweenUnderscores 61 | if not inBetweenUnderscores: 62 | locationsIdx += 1 63 | i = 0 if i == 1 else 1 64 | finalChars.append(string[stringIdx]) 65 | stringIdx += 1 66 | if locationsIdx < len(locations): 67 | finalChars.append("_") 68 | elif stringIdx < len(string): 69 | finalChars.append(string[stringIdx:]) 70 | return "".join(finalChars) 71 | -------------------------------------------------------------------------------- /Valid IP Addresses.py: -------------------------------------------------------------------------------- 1 | """ 2 | Valid IP Addresses 3 | 4 | You're given a string of length 12 or smaller,containing only digits.Write a function that returns all 5 | the possible IP addresses that can be created by inserting three . s in the string. 6 | 7 | An IP address is a sequence of four positive integers that are separated by .s, where each individual 8 | integer is within the range 0 - 255, inclusive. 9 | 10 | An Ip address isn't valid if any of the individual integers contains leading 0s. For example, "192.168.0.1" 11 | is a valid IP address, but "192.168.00.1" and "192.168.0.01" aren't,because they contain "00" and 01, 12 | respectively. Another example of a valid IP address is "99.1.1.10"; conversely, "991.1.1.0" isn't valid, 13 | because "991" is greater than 255. 14 | 15 | Your function should return the IP addresses in string format and in no particular order.If no valid IP 16 | addresses can be created from the string,your function should return an empty list. 17 | 18 | Note: check out our Systems Design Fundamentals on SystemsExpert to learn more about IP addresses! 19 | 20 | Sample Input 21 | string = "1921680" 22 | 23 | Sample Output 24 | [ 25 | "1.9.216.80", 26 | "1.92.16.80", 27 | "1.92.168.0", 28 | "19.2.16.80", 29 | "19.2.168.0", 30 | "19.21.6.80", 31 | "19.21.68.0", 32 | "19.216.8.0", 33 | "192.1.6.80", 34 | "192.1.68.0", 35 | "192.16.8.0", 36 | ] 37 | // The IP addresses could be ordered differently 38 | """ 39 | 40 | # SOLUTION 1 41 | 42 | # O(1) time | O(1) space 43 | def validIPAddresses(string): 44 | ipAddressesFound = [] 45 | for i in range(1, min(len(string), 4)): 46 | currentIPAddressParts = ["", "", "", ""] 47 | 48 | currentIPAddressParts[0] = string[:i] 49 | if not isValidPart(currentIPAddressParts[0]): 50 | continue 51 | 52 | for j in range(i + 1, i + min(len(string) - i, 4)): 53 | currentIPAddressParts[1] = string[i:j] 54 | if not isValidPart(currentIPAddressParts[1]): 55 | continue 56 | for k in range(j + 1, j + min(len(string) - j, 4)): 57 | currentIPAddressParts[2] = string[j:k] 58 | currentIPAddressParts[3] = string[k:] 59 | 60 | if isValidPart(currentIPAddressParts[2]) and isValidPart(currentIPAddressParts[3]): 61 | ipAddressesFound.append(".".join(currentIPAddressParts)) 62 | return ipAddressesFound 63 | 64 | 65 | def isValidPart(string): 66 | stringAsInt = int(string) 67 | if stringAsInt > 255: 68 | return False 69 | 70 | return len(string) == len(str(stringAsInt)) # check for leading 0 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Validate BST.py: -------------------------------------------------------------------------------- 1 | """ 2 | Validate a function that takes in a potentially invalid Binary Search Tree(BST) and returns 3 | a boolean representing whether the BST is valid. 4 | 5 | Each BST node has an integer value, a left child node, and a right child node.A node is sais 6 | to be valid BST node if and only if it satisfies the BST property:Its value is strictly greater 7 | than the values of every node to its left;its value is less than or equal to the values of every 8 | node to its right and its children nodes are either valid BST nodes themselves or None / null. 9 | 10 | A BST is valid if and only if all of its nodes are valid BST nodes. 11 | 12 | Sample Input 13 | 14 | 10 15 | / \ 16 | 5 15 17 | / \ / \ 18 | 2 5 13 22 19 | / \ 20 | 1 14 21 | 22 | 23 | Sample Output 24 | True 25 | """ 26 | 27 | # SOLUTION 1 28 | 29 | class BST: 30 | def __init__(self, value): 31 | self.value = value 32 | self.left = None 33 | self.right = None 34 | 35 | 36 | def validateBst(tree): 37 | return validateBstHelper(tree, float("-inf"), float("inf")) 38 | 39 | # O(n) time | O(d) space where d is longest depth of a branch 40 | def validateBstHelper(tree, minValue, maxValue): 41 | if tree is None: 42 | return True 43 | if tree.value < minValue or tree.value >= maxValue: 44 | return False 45 | leftIsValid = validateBstHelper(tree.left, minValue, tree.value) 46 | return leftIsValid and validateBstHelper(tree.right, tree.value, maxValue) 47 | -------------------------------------------------------------------------------- /Validate Subsequence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Validate Subsequence 3 | 4 | Given two non-empty arrays of integers,write a function that determines whether 5 | the second array is a subsequence of the first one. 6 | 7 | A subsequence of an array is a set of numbers that aren't necessarily adjacent 8 | in the array but that are in the same order as they appear in the array.For instance, 9 | the numbers [1, 3, 4] form a subsequence of the array [1, 2, 3, 4] and so do the numbers 10 | [2. 4].Note that a single number in the array and the array itself are both valid 11 | subsequence of the array. 12 | 13 | Sample Input 14 | array = [5, 1, 22, 25, 6, -1, 8,10] 15 | sequence = [1, 6 ,-1, 10] 16 | 17 | Sample Output 18 | True 19 | 20 | """ 21 | # SOLUTION 1 22 | 23 | # Time O(n) Space O(1) 24 | def validatedSub(array, subsequence): 25 | seq_id = 0 26 | for value in array: 27 | if value == subsequence[seq_id]: 28 | seq_id += 1 29 | 30 | if seq_id == len(subsequence): 31 | return True 32 | return False 33 | 34 | 35 | # SOLUTION 2 36 | 37 | # Time O(n) Space O(1) 38 | def validatedSub(array, subsequence): 39 | arr_id = 0 40 | seq_id = 0 41 | while arr_id < len(array) and seq_id < len(subsequence): 42 | if array[arr_id] == subsequence[seq_id]: 43 | seq_id += 1 44 | arr_id += 1 45 | 46 | return len(subsequence) == seq_id 47 | 48 | 49 | -------------------------------------------------------------------------------- /Water Area.py: -------------------------------------------------------------------------------- 1 | """ 2 | Water Area 3 | You're given an array of non-negative integers where each non-zero integer represents the height of a pillar of width 1. 4 | Imagine water being poured over all of the pillars; write a function that returns the surface area of the water trapped 5 | between the pillars viewed from the front. Note that spilled water should be ignored. 6 | You can refer to the first three minutes of this question's video explanation for a visual example. 7 | 8 | Sample Input 9 | heights = [0, 8, 0, 0, 5, 8, 9, 10, 0, 0, 1, 1, 0, 3] 10 | 11 | Sample Output 12 | 48 13 | // Below is a visual representation of the sample input. 14 | // The dots and vertical lines represent trapped water and pillars, respectively. 15 | // Note that there are 48 dots. 16 | // 17 | // | 18 | // | 19 | // |.....| 20 | // |.....| 21 | // |.....| 22 | // |..|..| 23 | // |..|..| 24 | // |..|..|.....| 25 | // |..|..|.....| 26 | // |..|..|..||.| 27 | 28 | """ 29 | 30 | # SOLUTION 1 31 | 32 | # O(n) time | O(n) space - where n is the length of the input array 33 | def waterArea(heights): 34 | maxes = [0 for x in heights] 35 | leftMax = 0 36 | for i in range(len(heights)): 37 | height = heights[i] 38 | maxes[i] = leftMax 39 | leftMax = max(leftMax, height) 40 | rightMax = 0 41 | for i in reversed(range(len(heights))): 42 | height = heights[i] 43 | minHeight = min(rightMax, maxes[i]) 44 | if height < minHeight: 45 | maxes[i] = minHeight - height 46 | else: 47 | maxes[i] = 0 48 | rightMax = max(rightMax, height) 49 | return sum(maxes) 50 | 51 | 52 | # SOLUTION 2 53 | 54 | # O(n) time | 0(1) space - where n is the length of the input array 55 | def waterArea(heights): 56 | if len(heights) == 0: 57 | return 0 58 | 59 | leftIdx = 0 60 | rightIdx = len(heights) - 1 61 | leftMax = heights[leftIdx] 62 | rightMax = heights[rightIdx] 63 | surfaceArea = 0 64 | 65 | while leftIdx < rightIdx: 66 | if heights[leftIdx] < heights[rightIdx]: 67 | leftIdx += 1 68 | leftMax = max(leftMax, heights[leftIdx]) 69 | surfaceArea += leftMax - heights[leftIdx] 70 | else: 71 | rightIdx -= 1 72 | rightMax = max(rightMax, heights[rightIdx]) 73 | surfaceArea += rightMax - heights[rightIdx] 74 | 75 | return surfaceArea 76 | -------------------------------------------------------------------------------- /Youngest Common Ancestor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Youngest Common Ancestor 3 | 4 | You're given three inputs,all of which are instances of an AncestralTree class 5 | that have an ancestor property pointing to their youngest ancestor.The first input 6 | is the top ancestor in an ancestral tree(i.e, the only instance that has no ancestor 7 | --its ancestor property points to None / null), and the other two points are descendants 8 | in the ancestral tree. 9 | 10 | Write a function that returns the youngest common ancestor to the two descendants. 11 | 12 | Note that a descendant is considered its own ancestor.So in the simple ancestral tree 13 | below,the youngest common ancestor to nodes A amd B is node A 14 | 15 | // The youngest common ancestor to nodes A and B is node A 16 | A 17 | / 18 | B 19 | 20 | Sample Input 21 | // The nodes are from ancestral tree below. 22 | topAncestor = node A 23 | descendantOne = node E 24 | descendantTwo = node I 25 | A 26 | / \ 27 | B C 28 | / \ / \ 29 | D E F G 30 | /\ 31 | H I 32 | 33 | Sample Output 34 | node B 35 | """ 36 | 37 | # SOLUTION 1 38 | 39 | # This is an input class. Do not edit. 40 | class AncestralTree: 41 | def __init__(self, name): 42 | self.name = name 43 | self.ancestor = None 44 | 45 | # O(d) time | O(1) space where d is the depth(height) of the ancestral tree 46 | def getYoungestCommonAncestor(topAncestor, descendantOne, descendantTwo): 47 | depthOne = getDescendantDepth(descendantOne, topAncestor) 48 | depthTwo = getDescendantDepth(descendantTwo, topAncestor) 49 | if depthOne > depthTwo: 50 | return backtrackAncestralTree(descendantOne, descendantTwo, depthOne - depthTwo) 51 | else: 52 | return backtrackAncestralTree(descendantTwo, descendantOne, depthTwo - depthOne) 53 | 54 | 55 | def getDescendantDepth(descendant, topAncestor): 56 | depth = 0 57 | while descendant != topAncestor: 58 | depth += 1 59 | descendant = descendant.ancestor 60 | return depth 61 | 62 | 63 | def backtrackAncestralTree(lowerDescendant, higherDescendant, diff): 64 | while diff > 0: 65 | lowerDescendant = lowerDescendant.ancestor 66 | diff -= 1 67 | while lowerDescendant != higherDescendant: 68 | lowerDescendant = lowerDescendant.ancestor 69 | higherDescendant = higherDescendant.ancestor 70 | return lowerDescendant 71 | 72 | 73 | -------------------------------------------------------------------------------- /Zigzag Traverse.py: -------------------------------------------------------------------------------- 1 | """ 2 | Zigzag Traverse 3 | 4 | Write a function that takes in an n x m two-dimensional array (that can be square-shaped when n==m) 5 | and returns a one-dimensional array of all the array's elements in zigzag order. 6 | Zigzag order starts at the top left corner of the two-dimensional array, goes down by one element, and 7 | proceeds in a zigzag pattern all the way to the bottom right corner. 8 | 9 | Sample Input 10 | 11 | array = [ 12 | [1, 3, 4, 10], 13 | [2, 5, 9, 11], 14 | [6, 8, 12, 15], 15 | [7, 13, 14, 16], 16 | ] 17 | 18 | Sample Output 19 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] 20 | 21 | """ 22 | # SOLUTION 1 23 | 24 | # O(n) time | 0(n) space where n is the total number of elements in the two-dimensional array 25 | def zigzagTraverse(array): 26 | height = len(array)-1 27 | width = len(array[0]) - 1 28 | result = [] 29 | row, col = 0,0 30 | goingDown = True 31 | while not isOutOfBounds(row, col, height, width): 32 | result.append(array[row][col]) 33 | if goingDown: 34 | if col == 0 or row == height: 35 | goingDown = False 36 | if row == height: 37 | col += 1 38 | else: 39 | row += 1 40 | else: 41 | row += 1 42 | col -= 1 43 | else: 44 | if row == 0 or col == width: 45 | goingDown = True 46 | if col == width: 47 | row += 1 48 | else: 49 | col += 1 50 | else: 51 | row -= 1 52 | col += 1 53 | return result 54 | 55 | def isOutOfBounds(row,col,height,width): 56 | return row < 0 or row > height or col < 0 or col > width 57 | -------------------------------------------------------------------------------- /Zip Linked List.py: -------------------------------------------------------------------------------- 1 | """ 2 | Zip Linked List 3 | You're given the head of a Singly Linked List of arbitrary length k. Write a function that zips the Linked List in 4 | place (i.e., doesn't create a 5 | brand new list) and returns its head. 6 | A Linked List is zipped if its nodes are in the following order, where k is the length of the Linked List: 7 | 1st node -> kth node -> 2nd node -> (k - 1)th node -> 3rd node -> (k - 2)th node -> ... 8 | Each LinkedList node has an integer value as well as a next node pointing to the next node in the list or to None / 9 | null if 10 | it's the tail of the list. 11 | You can assume that the input Linked List will always have at least one node; in other words, the head will never be 12 | None / null. 13 | 14 | Sample Input 15 | linkedList = 1 -> 2 -> 3 -> 4 -> 5 -> 6 // the head node with value 1 16 | Sample Output 17 | 1 -> 6 -> 2 -> 5 -> 3 -> 4 // the head node with value 1 18 | 19 | """ 20 | # SOLUTION 1 21 | 22 | # This is an input class. Do not edit. 23 | class LinkedList: 24 | def __init__(self, value): 25 | self.value = value 26 | self.next = None 27 | 28 | 29 | # O(n) time | 0(1) space where n is the length of the Linked List 30 | def zipLinkedList(linkedList): 31 | if linkedList.next is None or linkedList.next.next is None: 32 | return linkedList 33 | 34 | firstHalfHead = linkedList 35 | secondHalfHead = splitLinkedList(linkedList) 36 | reversedSecondHalfHead = reverseLinkedList(secondHalfHead) 37 | return interweaveLinkedLists(firstHalfHead, reversedSecondHalfHead) 38 | 39 | def splitLinkedList(linkedList): 40 | slowIterator = linkedList 41 | fastIterator = linkedList 42 | while fastIterator is not None and fastIterator.next is not None: 43 | slowIterator = slowIterator.next 44 | fastIterator = fastIterator.next.next 45 | secondHalfHead = slowIterator.next 46 | slowIterator.next = None 47 | return secondHalfHead 48 | 49 | def interweaveLinkedLists(linkedList1, linkedList2): 50 | linkedList1Iterator = linkedList1 51 | linkedList2Iterator = linkedList2 52 | while linkedList1Iterator is not None and linkedList2Iterator is not None: 53 | linkedList1IteratorNext = linkedList1Iterator.next 54 | linkedList2IteratorNext = linkedList2Iterator.next 55 | 56 | linkedList1Iterator.next = linkedList2Iterator 57 | linkedList2Iterator.next = linkedList1IteratorNext 58 | linkedList1Iterator = linkedList1IteratorNext 59 | linkedList2Iterator = linkedList2IteratorNext 60 | 61 | return linkedList1 62 | 63 | def reverseLinkedList(linkedlist): 64 | previousNode, currentNode = None, linkedlist 65 | while currentNode is not None: 66 | nextNode = currentNode.next 67 | currentNode.next = previousNode 68 | previousNode = currentNode 69 | currentNode = nextNode 70 | return previousNode 71 | 72 | --------------------------------------------------------------------------------