├── .gitignore ├── graph ├── 16_Shortest-Path.py └── 15_Build-Order.py ├── bit ├── 33_Bit-Int-Modulus.py ├── 34_Swap-Variables.py ├── 36_Rotate-Bits.py ├── 37_Ones-in-Binary.py ├── 35_Gray-Code.py └── 32_Two-Missing-Numbers.py ├── recursion ├── 19_Sum.py ├── 20_Reverse-Stack.py ├── 24_Balanced-Binary-Tree.py ├── 22_Longest-Consecutive-Branch.py ├── 26_Smallest-Change.py ├── 25_BST-Verification.py ├── 18_Lowest-Common-Ancestor.py ├── 23_Print-Reversed-Linked-List.py ├── 17_Random-Binary-Tree.py └── 21_Tree2Doubly-Linked-List.py ├── array ├── 12_Permutations.py ├── 14_Anagrams.py ├── 01_Median-of-Arrays.py ├── 08_Merge-K-Arrays.py ├── 06_Zero-Matrix.py ├── 05_Consecutive-Array.py ├── 02_0-1-Knapsack.py ├── 03_Matrix-Product.py ├── 04_Find-Duplicates.py ├── 10_Merge-Arrays.py ├── 11_Zero-Sum-Subarray.py ├── 09_Matrix-Search.py ├── 07_Square-Submatrix.py └── 13_N-Stacks.py ├── string ├── 49_Fibonacci-Number.py ├── 51_Kth-Most-Frequent-String.py ├── 46_String-Deletion.py ├── 48_String-Compression.py ├── 47_Longest-Common-Substring.py ├── 45_Autocomplete.py └── 50_Priority-Queue.py ├── stack ├── 28_Sort-Stacks.py ├── 29_Stack-from-Queues.py ├── 27_Inorder-Traversal.py ├── 30_Palindromes.py └── 31_Max-Stacks.py ├── linked_list ├── 38_Linked-List-Cycles.py ├── 44_Tree-Level-Order.py ├── 42_Nth-Last-Element.py ├── 39_Random-Linked-List.py ├── 40_Dedup-Linked-List.py ├── 41_Split-Linked-List.py └── 43_Three-Sum.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Environments 2 | venv/ 3 | .idea/ 4 | 5 | # Cache 6 | __pycache__ 7 | *.log 8 | *.pyc 9 | -------------------------------------------------------------------------------- /graph/16_Shortest-Path.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/20' 6 | 7 | Given a directed graph, find the shortest path between two nodes if one exists. 8 | ''' -------------------------------------------------------------------------------- /bit/33_Bit-Int-Modulus.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/27' 6 | 7 | Given a list of bytes a, each representing one byte of a larger integer (ie. {0x12, 0x34, 0x56, 0x78} represents the integer 0x12345678), and an integer b, find a % b. 8 | 9 | mod({0x03, 0xED}, 10) = 5 10 | ''' 11 | 12 | 13 | def mod(bits, num): 14 | res = 0 15 | for bit in bits: 16 | res = (res << 8) | (bit & 0xff) 17 | res %= num 18 | return res 19 | 20 | 21 | if __name__ == '__main__': 22 | assert mod([0x03, 0xED], 10) == 5 23 | -------------------------------------------------------------------------------- /bit/34_Swap-Variables.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/27' 6 | 7 | Given two integers, write a function that swaps them without using any temporary variables. 8 | ''' 9 | 10 | 11 | def swap1(num1, num2): 12 | num1 = num1 ^ num2 13 | num2 = num1 ^ num2 14 | num1 = num1 ^ num2 15 | return num1, num2 16 | 17 | 18 | def swap2(num1, num2): 19 | num1 = num1 + num2 20 | num2 = num1 - num2 21 | num1 = num1 - num2 22 | return num1, num2 23 | 24 | 25 | if __name__ == '__main__': 26 | for swap in [swap1, swap2]: 27 | assert swap(1, 2) == (2, 1) 28 | -------------------------------------------------------------------------------- /recursion/19_Sum.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/13' 6 | 7 | Given two integers, write a function to sum the numbers without using any arithmetic operators. 8 | ''' 9 | 10 | 11 | def add1(a, b): 12 | # recursive 13 | if not a: 14 | return b 15 | res = a ^ b 16 | carry = (a & b) << 1 17 | return add1(carry, res) 18 | 19 | 20 | def add2(a, b): 21 | # iterative 22 | carry, res = a, b 23 | while carry: 24 | tmp = res 25 | res = carry ^ res 26 | carry = (carry & tmp) << 1 27 | return res 28 | 29 | 30 | if __name__ == '__main__': 31 | for add in [add1, add2]: 32 | assert add(0, 137) == 137 33 | assert add(1, 3) == 4 34 | assert add(13, 27) == 40 35 | -------------------------------------------------------------------------------- /bit/36_Rotate-Bits.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/27' 6 | 7 | Given a number, write a function to rotate the bits (ie circular shift). 8 | 9 | rotate(0xFFFF0000, 8) = 0x00FFFF00 10 | rotate(0x13579BDF, 12) = 0xBDF13579 11 | rotate(0b10110011100011110000111110000000, 15) = 0b00011111000000010110011100011110 12 | ''' 13 | 14 | 15 | def rotate(xnum, k): 16 | # last k bits, first (32-k) bits 17 | res = xnum & ((1 << k) - 1) 18 | res = (res << (32 - k)) | (xnum >> k) 19 | return res 20 | 21 | 22 | if __name__ == '__main__': 23 | assert rotate(0xFFFF0000, 8) == 0x00FFFF00 24 | assert rotate(0x13579BDF, 12) == 0xBDF13579 25 | assert rotate(0b10110011100011110000111110000000, 15) == 0b00011111000000010110011100011110 26 | -------------------------------------------------------------------------------- /array/12_Permutations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Write a function that returns all permutations of a given list. 8 | 9 | permutations({1, 2, 3}) 10 | [1, 2, 3] 11 | [1, 3, 2] 12 | [2, 1, 3] 13 | [2, 3, 1] 14 | [3, 1, 2] 15 | [3, 2, 1] 16 | ''' 17 | 18 | solutions = [] 19 | 20 | 21 | def permutations(arr): 22 | _dfs(arr, []) 23 | return solutions 24 | 25 | 26 | def _dfs(arr, tmp): 27 | if len(tmp) == len(arr): 28 | return solutions.append(tmp) 29 | for num in arr: 30 | if num in tmp: 31 | continue 32 | _dfs(arr, tmp + [num]) 33 | 34 | 35 | if __name__ == '__main__': 36 | assert permutations([1, 2, 3]) == [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] 37 | -------------------------------------------------------------------------------- /bit/37_Ones-in-Binary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/27' 6 | 7 | Given an integer, write a function to compute the number of ones in the binary representation of the number. 8 | ''' 9 | 10 | 11 | def ones1(num): 12 | # operate util 0 13 | count = 0 14 | while num: 15 | num &= num-1 16 | count += 1 17 | return count 18 | 19 | 20 | def ones2(num): 21 | # judge last bit 22 | count = 0 23 | while num: 24 | count += num & 1 25 | num >>= 1 26 | return count 27 | 28 | 29 | if __name__ == '__main__': 30 | for ones in [ones1, ones2]: 31 | assert ones(0) == 0 32 | assert ones(1) == 1 33 | assert ones(2) == 1 34 | assert ones(3) == 2 35 | assert ones(7) == 3 36 | -------------------------------------------------------------------------------- /recursion/20_Reverse-Stack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/13' 6 | 7 | Given a stack, reverse the items without creating any additional data structures. 8 | 9 | Notes: https://blog.csdn.net/dm_vincent/article/details/8008238 10 | ''' 11 | 12 | 13 | def reverse(stack): 14 | # double recursive 15 | if not stack: 16 | return 17 | tmp = stack.pop() 18 | reverse(stack) 19 | _insert(stack, tmp) 20 | 21 | 22 | def _insert(stack, num): 23 | if not stack: 24 | stack.append(num) 25 | return 26 | tmp = stack.pop() 27 | _insert(stack, num) 28 | stack.append(tmp) 29 | 30 | 31 | if __name__ == '__main__': 32 | test = [1, 2, 3, 4, 5] 33 | 34 | reverse(test) 35 | assert test == [5, 4, 3, 2, 1] 36 | -------------------------------------------------------------------------------- /bit/35_Gray-Code.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/27' 6 | 7 | Given two integers, write a function to determine whether or not their binary representations differ by a single bit. 8 | 9 | gray(0, 1) = true 10 | gray(1, 2) = false 11 | ''' 12 | 13 | 14 | def gray1(num1, num2): 15 | # count '1' in XOR result 16 | calc = num1 ^ num2 17 | count = 0 18 | while calc: 19 | calc &= calc-1 20 | count += 1 21 | return count == 1 22 | 23 | 24 | def gray2(num1, num2): 25 | # no need to operate until 0 26 | calc = num1 ^ num2 27 | return calc and (calc & (calc-1) == 0) 28 | 29 | 30 | if __name__ == '__main__': 31 | for gray in [gray1, gray2]: 32 | assert gray(0, 1) 33 | assert not gray(1, 2) 34 | assert not gray(1, 1) 35 | -------------------------------------------------------------------------------- /string/49_Fibonacci-Number.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/12' 6 | 7 | Given an integer n, write a function to compute the nth Fibonacci number. 8 | 9 | fibonacci(1) = 1 10 | fibonacci(5) = 5 11 | fibonacci(10) = 55 12 | 13 | Notes: assume n positive number. 14 | ''' 15 | 16 | 17 | def fibonacci1(n): 18 | # recursive 19 | if n == 1 or n == 2: 20 | return 1 21 | return fibonacci1(n-2) + fibonacci1(n-1) 22 | 23 | 24 | def fibonacci2(n): 25 | # dp 26 | dp = [1 for _ in range(n)] 27 | for i in range(2, n): 28 | dp[i] = dp[i-2] + dp[i-1] 29 | return dp[n-1] 30 | 31 | 32 | if __name__ == '__main__': 33 | for fibonacci in [fibonacci1, fibonacci2]: 34 | assert fibonacci(1) == 1 35 | assert fibonacci(5) == 5 36 | assert fibonacci(10) == 55 37 | -------------------------------------------------------------------------------- /recursion/24_Balanced-Binary-Tree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/17' 6 | 7 | Given a binary tree, write a function to determine whether the tree is balanced. 8 | 9 | - all branches are same height(±1). 10 | - all subtrees are balanced. 11 | ''' 12 | 13 | 14 | class TreeNode(object): 15 | def __init__(self, val, left=None, right=None): 16 | self.val = val 17 | self.left = left 18 | self.right = right 19 | 20 | 21 | def is_balanced(root): 22 | return _balanced_height(root) > -1 23 | 24 | 25 | def _balanced_height(root): 26 | if not root: 27 | return 0 28 | h1 = _balanced_height(root.left) 29 | h2 = _balanced_height(root.right) 30 | if h1 == -1 or h2 == -1: 31 | return -1 32 | if abs(h1-h2) > 1: 33 | return -1 34 | return max(h1, h2) + 1 35 | -------------------------------------------------------------------------------- /recursion/22_Longest-Consecutive-Branch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/17' 6 | 7 | Given a tree, write a function to find the length of the longest branch of nodes in increasing consecutive order. 8 | ''' 9 | 10 | 11 | class TreeNode(object): 12 | def __init__(self, val, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | def lis(root): 19 | if not root: 20 | return 0 21 | return max(_helper(root.left, root.val, 1), _helper(root.right, root.val, 1)) 22 | 23 | 24 | def _helper(node, pre, length): 25 | if not node: 26 | return length 27 | if node.val == pre + 1: 28 | return max(_helper(node.left, node.val, length+1), _helper(node.right, node.val, length+1)) 29 | return max(_helper(node.left, node.val, 1), _helper(node.right, node.val, 1), length) 30 | -------------------------------------------------------------------------------- /stack/28_Sort-Stacks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Given a stack, sort the elements in the stack using one additional stack. 8 | ''' 9 | 10 | 11 | def sort1(stack): 12 | # two additional stacks 13 | buf1, buf2 = [], [] 14 | while stack: 15 | top = stack.pop() 16 | while buf1 and buf1[-1] > top: 17 | buf2.append(buf1.pop()) 18 | buf1.append(top) 19 | while buf2: 20 | buf1.append(buf2.pop()) 21 | return buf1 22 | 23 | 24 | def sort2(stack): 25 | # one additional stack 26 | buf = [] 27 | while stack: 28 | top = stack.pop() 29 | while buf and buf[-1] > top: 30 | stack.append(buf.pop()) 31 | buf.append(top) 32 | return buf 33 | 34 | 35 | if __name__ == '__main__': 36 | for sort in [sort1, sort2]: 37 | assert sort([1, 3, 2, 4]) == [1, 2, 3, 4] 38 | -------------------------------------------------------------------------------- /array/14_Anagrams.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Given two strings, write a function to determine whether they are anagrams. 8 | 9 | isAnagram("", "") = true 10 | isAnagram("A", "A") = true 11 | isAnagram("A", "B") = false 12 | isAnagram("ab", "ba") = true 13 | isAnagram("AB", "ab") = true 14 | 15 | Notes: ignore uppercase and characters' order 16 | ''' 17 | 18 | 19 | def is_anagram(s1, s2): 20 | # counting array[128] 21 | if len(s1) != len(s2): 22 | return False 23 | counts = [0 for _ in range(128)] 24 | for c in s1.lower(): 25 | counts[ord(c)] += 1 26 | for c in s2.lower(): 27 | counts[ord(c)] -= 1 28 | return not any(counts) 29 | 30 | 31 | if __name__ == '__main__': 32 | assert is_anagram('', '') 33 | assert is_anagram('A', 'A') 34 | assert not is_anagram('A', 'B') 35 | assert is_anagram('ab', 'ba') 36 | assert is_anagram('AB', 'ab') 37 | -------------------------------------------------------------------------------- /recursion/26_Smallest-Change.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/17' 6 | 7 | Given an input amount of change x, write a function to determine the minimum number of coins required to make that amount of change. 8 | 9 | Using American coins: 10 | change(1) = 1 11 | change(3) = 3 12 | change(7) = 3 13 | change(32) = 4 14 | ''' 15 | 16 | 17 | def change(amount, coins): 18 | # bottom-up dp 19 | largest = amount + 1 20 | dp = [largest for _ in range(largest)] 21 | dp[0] = 0 22 | for i in range(1, largest): 23 | for coin in coins: 24 | if i >= coin: 25 | dp[i] = min(dp[i], 1 + dp[i - coin]) 26 | return -1 if dp[amount] == largest else dp[amount] 27 | 28 | 29 | if __name__ == '__main__': 30 | test = [1, 5, 10, 25, 50, 100] 31 | 32 | assert change(1, test) == 1 33 | assert change(3, test) == 3 34 | assert change(7, test) == 3 35 | assert change(32, test) == 4 36 | -------------------------------------------------------------------------------- /array/01_Median-of-Arrays.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/23' 6 | 7 | Find the median of two sorted arrays. 8 | 9 | arr1 = [1, 3, 5] 10 | arr2 = [2, 4, 6] 11 | median(arr1, arr2) = 3.5 12 | ''' 13 | 14 | 15 | def median(arr1, arr2): 16 | # merge and get median 17 | m, n = len(arr1), len(arr2) 18 | arr, i, j = [], 0, 0 19 | while i < m and j < n: 20 | if arr1[i] < arr2[j]: 21 | arr.append(arr1[i]) 22 | i += 1 23 | else: 24 | arr.append(arr2[j]) 25 | j += 1 26 | arr.extend(arr1[i:] + arr2[j:]) 27 | total = m + n 28 | mid = total >> 1 29 | return arr[mid] if total&1 == 1 else (arr[mid-1]+arr[mid]) / 2.0 30 | 31 | 32 | if __name__ == '__main__': 33 | assert median([1, 3, 5], [2, 4, 6]) == 3.5 34 | assert median([1, 3], [2, 4, 5, 6]) == 3.5 35 | assert median([1, 3, 5], [2, 4]) == 3 36 | assert median([], [1, 2, 3, 4, 5]) == 3 37 | -------------------------------------------------------------------------------- /linked_list/38_Linked-List-Cycles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/28' 6 | 7 | Given a linked list, determine whether it contains a cycle. 8 | 9 | 1 -> 2 -> 3 -> 4 10 | ^ | 11 | |_________| 12 | ''' 13 | 14 | 15 | class LinkNode(object): 16 | def __init__(self, val, next=None): 17 | self.val = val 18 | self.next = next 19 | 20 | 21 | def contains(head): 22 | # Floyd Cycle Detection Algorithm 23 | slow, fast = head, head 24 | while fast and fast.next: 25 | slow = slow.next 26 | fast = fast.next.next 27 | if slow == fast: 28 | return True 29 | return False 30 | 31 | 32 | if __name__ == '__main__': 33 | test = LinkNode(1) 34 | test.next = LinkNode(2) 35 | test.next.next = LinkNode(3) 36 | test.next.next.next = LinkNode(4) 37 | assert not contains(test) 38 | 39 | test.next.next.next.next = test.next 40 | assert contains(test) 41 | -------------------------------------------------------------------------------- /array/08_Merge-K-Arrays.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/25' 6 | 7 | Given k sorted arrays, merge them into a single sorted array. 8 | 9 | merge({{1, 4, 7},{2, 5, 8},{3, 6, 9}}) = {1, 2, 3, 4, 5, 6, 7, 8, 9} 10 | ''' 11 | from functools import reduce 12 | 13 | 14 | def merge(arrs): 15 | # each time merge two arrays 16 | return reduce(_merge2, arrs, []) 17 | 18 | 19 | def _merge2(arr1, arr2): 20 | res = [] 21 | i, j, m, n = 0, 0, len(arr1), len(arr2) 22 | while i < m and j < n: 23 | if arr1[i] < arr2[j]: 24 | res.append(arr1[i]) 25 | i += 1 26 | else: 27 | res.append(arr2[j]) 28 | j += 1 29 | res.extend(arr1[i:] + arr2[j:]) 30 | return res 31 | 32 | 33 | if __name__ == '__main__': 34 | assert merge([[]]) == [] 35 | assert merge([[1, 4, 5], [], [2, 3, 6, 7]]) == [1, 2, 3, 4, 5, 6, 7] 36 | assert merge([[1, 4, 7], [2, 5, 8], [3, 6, 9]]) == [1, 2, 3, 4, 5, 6, 7, 8, 9] 37 | -------------------------------------------------------------------------------- /stack/29_Stack-from-Queues.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Implement a LIFO stack with basic functionality (push and pop) using FIFO queues to store the data. 8 | ''' 9 | 10 | 11 | class Stack(object): 12 | def __init__(self): 13 | self.store = [] 14 | 15 | def push(self, x): 16 | self.store.append(x) 17 | for _ in range(len(self.store) - 1): 18 | self.store.append(self.store.pop(0)) 19 | 20 | def pop(self): 21 | if not self.store: 22 | raise IndexError 23 | return self.store.pop(0) 24 | 25 | 26 | if __name__ == '__main__': 27 | stack = Stack() 28 | 29 | stack.push(1) 30 | stack.push(3) 31 | stack.push(5) 32 | assert stack.pop() == 5 33 | stack.push(5) 34 | assert stack.pop() == 5 35 | assert stack.pop() == 3 36 | assert stack.pop() == 1 37 | 38 | try: 39 | stack.pop() 40 | except Exception as e: 41 | assert type(e) == IndexError 42 | -------------------------------------------------------------------------------- /string/51_Kth-Most-Frequent-String.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/12' 6 | 7 | Given a list of strings, write a function to get the kth most frequently occurring string. 8 | 9 | kthMostFrequent({"a","b","c","a","b","a"}, 0) = "a" 10 | kthMostFrequent({"a","b","c","a","b","a"}, 1) = "b" 11 | kthMostFrequent({"a","b","c","a","b","a"}, 2) = "c" 12 | kthMostFrequent({"a","b","c","a","b","a"}, 3) = null 13 | ''' 14 | 15 | 16 | def kth_frequent(arr, k): 17 | # hashmap 18 | d = {} 19 | for s in arr: 20 | d[s] = d.get(s, 0) + 1 21 | items = sorted(d.items(), key=lambda item: item[1], reverse=True) 22 | if k >= len(items): 23 | return None 24 | return items[k][0] 25 | 26 | 27 | if __name__ == '__main__': 28 | assert kth_frequent(['a', 'b', 'c', 'a', 'b', 'a'], 0) == 'a' 29 | assert kth_frequent(['a', 'b', 'c', 'a', 'b', 'a'], 1) == 'b' 30 | assert kth_frequent(['a', 'b', 'c', 'a', 'b', 'a'], 2) == 'c' 31 | assert not kth_frequent(['a', 'b', 'c', 'a', 'b', 'a'], 3) 32 | -------------------------------------------------------------------------------- /linked_list/44_Tree-Level-Order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/29' 6 | 7 | Given a tree, write a function that prints out the nodes of the tree in level order. 8 | ''' 9 | 10 | 11 | class TreeNode(object): 12 | def __init__(self, val, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | def level_order(root): 19 | res, queue = [], root and [root] 20 | while queue: 21 | cur = queue.pop(0) 22 | res.append(cur.val) 23 | if cur.left: 24 | queue.append(cur.left) 25 | if cur.right: 26 | queue.append(cur.right) 27 | return res 28 | 29 | 30 | if __name__ == '__main__': 31 | test = TreeNode(1) 32 | test.left = TreeNode(2) 33 | test.right = TreeNode(3) 34 | test.left.left = TreeNode(4) 35 | test.left.right = TreeNode(5) 36 | test.right.left = TreeNode(6) 37 | test.right.right = TreeNode(7) 38 | 39 | assert level_order(test) == [1, 2, 3, 4, 5, 6, 7] 40 | -------------------------------------------------------------------------------- /string/46_String-Deletion.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/3' 6 | 7 | Given a string and a dictionary HashSet, write a function to determine the minimum number of characters to delete to make a word. 8 | 9 | dictionary: [“a”, “aa”, “aaa”] 10 | query: “abc” 11 | output: 2 12 | 13 | Notes: delete one character each time. 14 | ''' 15 | 16 | 17 | def min_deletion(bag, word): 18 | queue, uniques = [word], {word} 19 | while queue: 20 | cur = queue.pop(0) 21 | if cur in bag: 22 | return len(word) - len(cur) 23 | for i in range(len(cur)): 24 | nxt = cur[:i] + cur[i+1:] 25 | if nxt and nxt not in uniques: 26 | queue.append(nxt) 27 | uniques.add(nxt) 28 | return -1 29 | 30 | 31 | if __name__ == '__main__': 32 | test = {'a', 'aa', 'aaa'} 33 | 34 | assert min_deletion(test, 'abc') == 2 35 | assert min_deletion(test, 'bac') == 2 36 | assert min_deletion(test, 'aba') == 1 37 | assert min_deletion(test, 'd') == -1 38 | -------------------------------------------------------------------------------- /stack/27_Inorder-Traversal.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Given a binary search tree, print out the elements of the tree in order without using recursion. 8 | ''' 9 | 10 | 11 | class TreeNode(object): 12 | def __init__(self, val, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | def inorder_traversal(root): 19 | res, stack = [], [] 20 | while root or stack: 21 | if root: 22 | stack.append(root) 23 | root = root.left 24 | else: 25 | tmp = stack.pop() 26 | res.append(tmp.val) 27 | root = tmp.right 28 | return res 29 | 30 | 31 | if __name__ == '__main__': 32 | test = TreeNode(5) 33 | test.left = TreeNode(2) 34 | test.left.left = TreeNode(1) 35 | test.left.right = TreeNode(3) 36 | test.right = TreeNode(7) 37 | test.right.left = TreeNode(6) 38 | test.right.right = TreeNode(8) 39 | 40 | assert inorder_traversal(test) == [1, 2, 3, 5, 6, 7, 8] 41 | -------------------------------------------------------------------------------- /recursion/25_BST-Verification.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/20' 6 | 7 | Given a binary tree, write a function to test if the tree is a binary search tree. 8 | 9 | Notes: node.left.val <= node.val < node.right.val 10 | ''' 11 | import sys 12 | 13 | 14 | class TreeNode(object): 15 | def __init__(self, val, left=None, right=None): 16 | self.val = val 17 | self.left = left 18 | self.right = right 19 | 20 | 21 | def is_bst(root): 22 | return _helper(root, -sys.maxsize-1, sys.maxsize) 23 | 24 | 25 | def _helper(node, a, b): 26 | if not node: 27 | return True 28 | if node.val <= a or node.val > b: 29 | return False 30 | return _helper(node.left, a, node.val) and _helper(node.right, node.val, b) 31 | 32 | 33 | if __name__ == '__main__': 34 | test = TreeNode(2) 35 | test.left = TreeNode(2) 36 | test.left.left = TreeNode(1) 37 | test.right = TreeNode(3) 38 | 39 | assert is_bst(None) 40 | assert is_bst(test) 41 | 42 | test.right.val = 2 43 | assert not is_bst(test) 44 | -------------------------------------------------------------------------------- /string/48_String-Compression.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/12' 6 | 7 | Given a string, write a function to compress it by shortening every sequence of the same character to that character followed by the number of repetitions. If the compressed string is longer than the original, you should return the original string. 8 | 9 | compress(“a”) = "a" 10 | compress(“aaa”) = "a3" 11 | compress(“aaabbb”) = "a3b3" 12 | compress(“aaabccc”) = "a3b1c3" 13 | ''' 14 | 15 | 16 | def compress(s): 17 | # count and say 18 | res = '' 19 | pre, count = '#', 0 20 | for i in range(len(s)): 21 | if s[i] == pre: 22 | count += 1 23 | else: 24 | if pre != '#': 25 | res += pre + str(count) 26 | pre, count = s[i], 1 27 | if i == len(s)-1: 28 | res += pre + str(count) 29 | return res if len(res) <= len(s) else s 30 | 31 | 32 | if __name__ == '__main__': 33 | assert compress('a') == 'a' 34 | assert compress('aa') == 'a2' 35 | assert compress('aaabccc') == 'a3b1c3' 36 | -------------------------------------------------------------------------------- /linked_list/42_Nth-Last-Element.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/28' 6 | 7 | Given a linked list, and an input n, write a function that returns the nth-to-last element of the linked list. 8 | 9 | list = 1 -> 2 -> 3 -> 4 -> 5 -> null 10 | nthToLast(list, 0) = 5 11 | nthToLast(list, 1) = 4 12 | nthToLast(list, 4) = 1 13 | nthToLast(list, 5) = null 14 | ''' 15 | 16 | 17 | class LinkNode(object): 18 | def __init__(self, val, next=None): 19 | self.val = val 20 | self.next = next 21 | 22 | 23 | def nth2last(head, n): 24 | fir, sec = head, head 25 | for _ in range(n): 26 | if not sec: 27 | return None 28 | sec = sec.next 29 | if not sec: 30 | return None 31 | while sec.next: 32 | fir, sec = fir.next, sec.next 33 | return fir 34 | 35 | 36 | if __name__ == '__main__': 37 | test = LinkNode(1) 38 | test.next = LinkNode(2) 39 | test.next.next = LinkNode(3) 40 | test.next.next.next = LinkNode(4) 41 | test.next.next.next.next = LinkNode(5) 42 | 43 | assert nth2last(test, 0).val == 5 44 | assert nth2last(test, 1).val == 4 45 | assert nth2last(test, 4).val == 1 46 | assert not nth2last(test, 5) 47 | -------------------------------------------------------------------------------- /recursion/18_Lowest-Common-Ancestor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/13' 6 | 7 | Given two nodes in a binary tree, write a function to find the lowest common ancestor. 8 | ''' 9 | 10 | 11 | class TreeNode(object): 12 | def __init__(self, val, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | def lca(root, node1, node2): 19 | if not root: 20 | return None 21 | if root == node1 or root == node2: 22 | return root 23 | left_res = lca(root.left, node1, node2) 24 | right_res = lca(root.right, node1, node2) 25 | if left_res and right_res: 26 | return root 27 | return left_res or right_res 28 | 29 | 30 | if __name__ == '__main__': 31 | test = TreeNode(1) 32 | test.left = TreeNode(2) 33 | test.right = TreeNode(3) 34 | test.left.left = TreeNode(4) 35 | test.left.right = TreeNode(5) 36 | test.right.left = TreeNode(6) 37 | test.right.right = TreeNode(7) 38 | 39 | left1 = test.left.left 40 | left2 = test.left.right 41 | right1 = test.right.left 42 | assert lca(test, left1, right1) == test 43 | assert lca(test, left1, left2) == test.left 44 | -------------------------------------------------------------------------------- /linked_list/39_Random-Linked-List.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/28' 6 | 7 | Given a linked list where each node has two pointers, one to the next node and one to a random node in the list, clone the linked list. 8 | 9 | 1 -> 2 -> 3 -> 4 -> null 10 | | | | | 11 | v v v v 12 | 3 1 3 2 13 | ''' 14 | from random import randint 15 | 16 | 17 | class LinkNode(object): 18 | def __init__(self, val, next=None, rand=None): 19 | self.val = val 20 | self.next = next 21 | self.rand = rand 22 | 23 | 24 | def clone(head): 25 | dup = head 26 | cur = dup 27 | while cur: 28 | cur.rand = _get_random(head) 29 | cur = cur.next 30 | return dup 31 | 32 | 33 | def _get_random(head): 34 | # Reservoir Sampling 35 | cur, r, i = head, None, 0 36 | while cur: 37 | if randint(0, i) == 0: 38 | r = cur 39 | cur, i = cur.next, i+1 40 | return r 41 | 42 | 43 | if __name__ == '__main__': 44 | test = LinkNode(1) 45 | test.next = LinkNode(2) 46 | test.next.next = LinkNode(3) 47 | test.next.next.next = LinkNode(4) 48 | 49 | node = clone(test) 50 | while node: 51 | print(node.val, '->', node.rand.val) 52 | node = node.next 53 | -------------------------------------------------------------------------------- /array/06_Zero-Matrix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/24' 6 | 7 | Given a boolean matrix, update it so that if any cell is true, all the cells in that row and column are true. 8 | 9 | [true, false, false] [true, true, true ] 10 | [false, false, false] -> [true, false, false] 11 | [false, false, false] [true, false, false] 12 | ''' 13 | 14 | 15 | def zero_matrix(matrix): 16 | if not matrix or not matrix[0]: 17 | return 18 | 19 | m, n = len(matrix), len(matrix[0]) 20 | points = [] 21 | for i in range(m): 22 | for j in range(n): 23 | if matrix[i][j]: 24 | points.append((i, j)) 25 | 26 | for i, j in points: 27 | for x in range(m): 28 | matrix[x][j] = True 29 | for y in range(n): 30 | matrix[i][y] = True 31 | 32 | 33 | if __name__ == '__main__': 34 | matrix1 = [[True, False, False], [False, False, False], [False, False, False]] 35 | zero_matrix(matrix1) 36 | assert matrix1 == [[True, True, True], [True, False, False], [True, False, False]] 37 | 38 | matrix2 = [[True, False, True], [False, False, False], [False, False, False]] 39 | zero_matrix(matrix2) 40 | assert matrix2 == [[True, True, True], [True, False, True], [True, False, True]] 41 | -------------------------------------------------------------------------------- /array/05_Consecutive-Array.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/24' 6 | 7 | Given an unsorted array, find the length of the longest sequence of consecutive numbers in the array. 8 | 9 | consecutive([4, 2, 1, 6, 5]) = 3, [4, 5, 6] 10 | consecutive([5, 5, 3, 1]) = 1, [1], [3], or [5] 11 | ''' 12 | 13 | 14 | def consecutive1(nums): 15 | # sort and count: O(nlogn) time, O(1) space 16 | if not nums: 17 | return 0 18 | nums.sort() 19 | res, cur = 1, 1 20 | for i in range(1, len(nums)): 21 | if nums[i] - nums[i-1] == 1: 22 | cur += 1 23 | else: 24 | res = max(res, cur) 25 | cur = 1 26 | res = max(res, cur) 27 | return res 28 | 29 | 30 | def consecutive2(nums): 31 | # hashset: O(n) time, O(n) space 32 | uniques = set(nums) 33 | res = 0 34 | for unique in uniques: 35 | # not the leftmost value in the sequence 36 | if unique - 1 in uniques: 37 | continue 38 | cur = 0 39 | while unique in uniques: 40 | unique, cur = unique + 1, cur + 1 41 | res = max(res, cur) 42 | return res 43 | 44 | 45 | if __name__ == '__main__': 46 | for consecutive in [consecutive1, consecutive2]: 47 | assert consecutive([]) == 0 48 | assert consecutive([4, 2, 1, 6, 5]) == 3 49 | assert consecutive([5, 5, 3, 1]) == 1 50 | -------------------------------------------------------------------------------- /array/02_0-1-Knapsack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/24' 6 | 7 | Given a list of items with values and weights, as well as a max weight, find the maximum value you can generate from items where the sum of the weights is less than the max. 8 | 9 | items = {(w:1, v:6), (w:2, v:10), (w:3, v:12)} 10 | maxWeight = 5 11 | knapsack(items, maxWeight) = 22 12 | 13 | Notes: no duplicate weights, used only once. 14 | ''' 15 | 16 | 17 | class Solution(object): 18 | def knapsack(self, items, max_weight): 19 | # backtracking: find best solution 20 | self.max_sum = 0 21 | self._dfs(items, max_weight, 0, len(items), 0) 22 | return self.max_sum 23 | 24 | def _dfs(self, items, target, pos, n, cur_sum): 25 | if target < 0: 26 | return 27 | if target == 0: 28 | self.max_sum = max(self.max_sum, cur_sum) 29 | return 30 | for i in range(pos, n): 31 | weight, value = items[i] 32 | self._dfs(items, target-weight, i+1, n, cur_sum+value) 33 | 34 | 35 | if __name__ == '__main__': 36 | test = Solution() 37 | 38 | assert test.knapsack([(1, 6), (2, 10), (3, 12)], 5) == 22 39 | assert test.knapsack([(1, 6), (2, 10), (4, 12)], 7) == 28 40 | assert test.knapsack([(1, 6), (2, 10), (3, 12), (4, 18)], 5) == 24 41 | assert test.knapsack([(1, 6), (2, 10), (3, 12), (4, 16)], 4) == 18 42 | -------------------------------------------------------------------------------- /array/03_Matrix-Product.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/24' 6 | 7 | Given a matrix, find the path from top left to bottom right with the greatest product by moving only down and right. 8 | 9 | [1, 2, 3] 10 | [4, 5, 6] 11 | [7, 8, 9] 12 | 1 -> 4 -> 7 -> 8 -> 9 13 | 2016 14 | 15 | [-1, 2, 3] 16 | [4, 5, -6] 17 | [7, 8, 9] 18 | -1 -> 4 -> 5 -> -6 -> 9 19 | 1080 20 | 21 | Notes: brute force O(2^(m+n-2)) 22 | ''' 23 | 24 | 25 | def matrix_product(matrix): 26 | # dp: [min_cur, max_cur] 27 | if not matrix or not matrix[0]: 28 | return 0 29 | 30 | m, n = len(matrix), len(matrix[0]) 31 | dp = [[[_, _] for _ in range(n)] for _ in range(m)] 32 | dp[0][0] = [matrix[0][0], matrix[0][0]] 33 | for i in range(1, m): 34 | dp[i][0] = sorted(pre * matrix[i][0] for pre in dp[i-1][0]) 35 | for j in range(1, n): 36 | dp[0][j] = sorted(pre * matrix[0][j] for pre in dp[0][j-1]) 37 | 38 | for i in range(1, m): 39 | for j in range(1, n): 40 | tmp = sorted(pre * matrix[i][j] for pre in dp[i-1][j] + dp[i][j-1]) 41 | dp[i][j] = [tmp[0], tmp[-1]] 42 | 43 | return dp[-1][-1][-1] 44 | 45 | 46 | if __name__ == '__main__': 47 | matrix1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 48 | assert matrix_product(matrix1) == 2016 49 | 50 | matrix2 = [[-1, 2, 3], [4, 5, -6], [7, 8, 9]] 51 | assert matrix_product(matrix2) == 1080 52 | -------------------------------------------------------------------------------- /linked_list/40_Dedup-Linked-List.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/28' 6 | 7 | Given an unsorted linked list, write a function to remove all the duplicates. 8 | 9 | dedup(1 -> 2 -> 3 -> 2 -> 1) = 1 -> 2 -> 3 10 | ''' 11 | 12 | 13 | class LinkNode(object): 14 | def __init__(self, val, next=None): 15 | self.val = val 16 | self.next = next 17 | 18 | 19 | def dedup1(head): 20 | # hashset 21 | unq = set() 22 | res, pre = head, None 23 | while head: 24 | if head.val in unq: 25 | pre.next = head.next 26 | else: 27 | unq.add(head.val) 28 | pre = head 29 | head = head.next 30 | return res 31 | 32 | 33 | def dedup2(head): 34 | # brute 35 | res = head 36 | while head: 37 | cur = head 38 | while cur.next: 39 | if cur.next.val == cur.val: 40 | cur.next = cur.next.next 41 | else: 42 | cur = cur.next 43 | head = head.next 44 | return res 45 | 46 | 47 | if __name__ == '__main__': 48 | test = LinkNode(1) 49 | test.next = LinkNode(2) 50 | test.next.next = LinkNode(3) 51 | test.next.next.next = LinkNode(2) 52 | test.next.next.next.next = LinkNode(1) 53 | 54 | for dedup in [dedup1, dedup2]: 55 | node = dedup(test) 56 | while node: 57 | print(node.val) 58 | node = node.next 59 | -------------------------------------------------------------------------------- /recursion/23_Print-Reversed-Linked-List.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/17' 6 | 7 | Given a linked list, write a function that prints the nodes of the list in reverse order. 8 | 9 | printReversedList(1 -> 2 -> 3) 10 | 3 11 | 2 12 | 1 13 | ''' 14 | from copy import deepcopy 15 | 16 | 17 | class LinkNode(object): 18 | def __init__(self, val, next=None): 19 | self.val = val 20 | self.next = next 21 | 22 | 23 | def print_rl1(head): 24 | # auxiliary stack 25 | stack = [] 26 | while head: 27 | stack.append(head.val) 28 | head = head.next 29 | while stack: 30 | print(stack.pop()) 31 | 32 | 33 | def print_rl2(head): 34 | # iterative 35 | new_head = None 36 | while head: 37 | tmp = head.next 38 | head.next = new_head 39 | new_head = head 40 | head = tmp 41 | while new_head: 42 | print(new_head.val) 43 | new_head = new_head.next 44 | 45 | 46 | def print_rl3(head): 47 | # recursive 48 | if not head: 49 | return 50 | print_rl3(head.next) 51 | print(head.val) 52 | 53 | 54 | if __name__ == '__main__': 55 | test = LinkNode(1) 56 | test.next = LinkNode(2) 57 | test.next.next = LinkNode(3) 58 | 59 | for print_func in [print_rl1, print_rl2, print_rl3]: 60 | dup = deepcopy(test) 61 | print('Output of {}:'.format(print_func.__name__)) 62 | print_func(dup) 63 | -------------------------------------------------------------------------------- /graph/15_Build-Order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/20' 6 | 7 | Given a list of packages that need to be built and the dependencies for each package, determine a valid order in which to build the packages. 8 | 9 | 0: 10 | 1: 0 11 | 2: 0 12 | 3: 1, 2 13 | 4: 3 14 | 15 | output: 4, 3, 1, 2, 0 16 | ''' 17 | from collections import defaultdict 18 | 19 | 20 | class Graph(object): 21 | def __init__(self, n_vertices): 22 | self.graph = defaultdict(list) 23 | self.V = n_vertices 24 | 25 | def add_edge(self, u, v): 26 | self.graph[u].append(v) 27 | 28 | def top_sort(self): 29 | indegrees = [0 for _ in range(self.V)] 30 | for i in self.graph: 31 | for j in self.graph[i]: 32 | indegrees[j] += 1 33 | queue = [] 34 | for i, indegree in enumerate(indegrees): 35 | if indegree == 0: 36 | queue.append(i) 37 | order = [] 38 | while queue: 39 | u = queue.pop(0) 40 | order.append(u) 41 | for i in self.graph[u]: 42 | indegrees[i] -= 1 43 | if indegrees[i] == 0: 44 | queue.append(i) 45 | return order 46 | 47 | 48 | if __name__ == '__main__': 49 | test = Graph(5) 50 | test.add_edge(1, 0) 51 | test.add_edge(2, 0) 52 | test.add_edge(3, 1) 53 | test.add_edge(3, 2) 54 | test.add_edge(4, 3) 55 | 56 | assert test.top_sort() == [4, 3, 1, 2, 0] 57 | -------------------------------------------------------------------------------- /string/47_Longest-Common-Substring.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/7' 6 | 7 | Given two strings, write a function that returns the longest common substring. 8 | 9 | longestSubstring("ABAB", "BABA") = "ABA" 10 | ''' 11 | 12 | 13 | def lcs1(s1, s2): 14 | # brute 15 | l1, l2 = len(s1), len(s2) 16 | start, max_len = 0, 0 17 | for i in range(l1): 18 | for j in range(l2): 19 | cur1, cur2, cur_len = i, j, 0 20 | while cur1 < l1 and cur2 < l2 and s1[cur1] == s2[cur2]: 21 | cur1, cur2, cur_len = cur1+1, cur2+1, cur_len+1 22 | if cur_len > max_len: 23 | start, max_len = i, cur_len 24 | return s1[start:start+max_len] 25 | 26 | 27 | def lcs2(s1, s2): 28 | # dp(cache) 29 | l1, l2 = len(s1), len(s2) 30 | end, max_len = 0, 0 31 | cache = [[0 for _ in range(l2)] for _ in range(l1)] 32 | for i in range(l1): 33 | for j in range(l2): 34 | if s1[i] == s2[j]: 35 | if i == 0 or j == 0: 36 | cache[i][j] = 1 37 | else: 38 | cache[i][j] = cache[i-1][j-1] + 1 39 | if cache[i][j] > max_len: 40 | end, max_len = i+1, cache[i][j] 41 | return s1[end-max_len:end] 42 | 43 | 44 | if __name__ == '__main__': 45 | for lcs in [lcs1, lcs2]: 46 | assert lcs('ABC', 'DEF') == '' 47 | assert lcs('ABAB', 'BABA') == 'ABA' 48 | assert lcs('ABAB', 'BABAB') == 'ABAB' 49 | -------------------------------------------------------------------------------- /recursion/17_Random-Binary-Tree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/13' 6 | 7 | Implement a binary tree with a method getRandomNode() that returns a random node. 8 | 9 | getRandomNode() = 5 10 | getRandomNode() = 8 11 | getRandomNode() = 1 12 | ''' 13 | from random import randint 14 | 15 | 16 | class TreeNode(object): 17 | def __init__(self, val, left=None, right=None): 18 | self.val = val 19 | self.left = left 20 | self.right = right 21 | 22 | 23 | def random_node(root): 24 | # LevelOrder + ReservoirSampling 25 | r, i = None, 0 26 | queue = root and [root] 27 | while queue: 28 | cur = queue.pop(0) 29 | if randint(0, i) == 0: 30 | r = cur 31 | if cur.left: 32 | queue.append(cur.left) 33 | if cur.right: 34 | queue.append(cur.right) 35 | i += 1 36 | return r 37 | 38 | 39 | if __name__ == '__main__': 40 | test = TreeNode(1) 41 | test.left = TreeNode(2) 42 | test.right = TreeNode(3) 43 | test.left.left = TreeNode(4) 44 | test.left.right = TreeNode(5) 45 | test.right.left = TreeNode(6) 46 | test.right.right = TreeNode(7) 47 | 48 | n_experiments = 100000 49 | res_probability = 1 / 7 50 | res_arr = [0 for _ in range(7)] 51 | for _ in range(n_experiments): 52 | res = random_node(test) 53 | res_arr[res.val-1] += 1 54 | for res in res_arr: 55 | assert round(res / n_experiments, 2) == round(res_probability, 2) 56 | -------------------------------------------------------------------------------- /bit/32_Two-Missing-Numbers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/27' 6 | 7 | Given an array containing all the numbers from 1 to n except two, find the two missing numbers. 8 | 9 | missing([4, 2, 3]) = 1, 5 10 | ''' 11 | from functools import reduce 12 | from collections import Counter 13 | 14 | 15 | def missing1(nums): 16 | # sort and count 17 | nums.sort() 18 | count, i, res = 0, 0, [] 19 | while i < len(nums): 20 | if i+count+1 != nums[i]: 21 | count += 1 22 | res.append(i+count) 23 | i += 1 24 | while count < 2: 25 | count += 1 26 | res.append(i+count) 27 | return res 28 | 29 | 30 | def missing2(nums): 31 | # convert to Single Number III (Leetcode) 32 | n = len(nums) + 2 33 | arr = nums + list(range(1, n+1)) 34 | calc = reduce(lambda x, y: x ^ y, arr) 35 | # get last '1' bit 36 | calc &= -calc 37 | res = [0, 0] 38 | for num in arr: 39 | if num & calc: 40 | res[0] ^= num 41 | else: 42 | res[1] ^= num 43 | return res 44 | 45 | 46 | if __name__ == '__main__': 47 | test1 = [] 48 | test2 = [4, 2, 3] 49 | test3 = [5, 1, 3] 50 | 51 | for missing in [missing1, missing2]: 52 | dup1 = test1[:] 53 | assert Counter(missing(dup1)) == Counter([1, 2]) 54 | dup2 = test2[:] 55 | assert Counter(missing(dup2)) == Counter([1, 5]) 56 | dup3 = test3[:] 57 | assert Counter(missing(dup3)) == Counter([2, 4]) 58 | -------------------------------------------------------------------------------- /linked_list/41_Split-Linked-List.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/28' 6 | 7 | Given a linked list, write a function to split the list into two equal halves. 8 | 9 | divide(1 -> 2 -> 3 -> 4) = 1 -> 2, 3 -> 4 10 | divide(1 -> 2 -> 3 -> 4 -> 5) = 1 -> 2 -> 3, 4 -> 5 11 | ''' 12 | from copy import deepcopy 13 | 14 | 15 | class LinkNode(object): 16 | def __init__(self, val, next=None): 17 | self.val = val 18 | self.next = next 19 | 20 | 21 | def divide(head): 22 | fir, slow, fast = head, head, head.next 23 | while fast and fast.next: 24 | fast = fast.next.next 25 | slow = slow.next 26 | sec = slow.next 27 | slow.next = None 28 | return fir, sec 29 | 30 | 31 | if __name__ == '__main__': 32 | test = LinkNode(1) 33 | test.next = LinkNode(2) 34 | test.next.next = LinkNode(3) 35 | test.next.next.next = LinkNode(4) 36 | 37 | test1 = deepcopy(test) 38 | fir1, sec1 = divide(test1) 39 | assert fir1.val == 1 40 | assert fir1.next.val == 2 41 | assert not fir1.next.next 42 | assert sec1.val == 3 43 | assert sec1.next.val == 4 44 | assert not sec1.next.next 45 | 46 | test2 = deepcopy(test) 47 | test2.next.next.next.next = LinkNode(5) 48 | fir2, sec2 = divide(test2) 49 | assert fir2.val == 1 50 | assert fir2.next.val == 2 51 | assert fir2.next.next.val == 3 52 | assert not fir2.next.next.next 53 | assert sec2.val == 4 54 | assert sec2.next.val == 5 55 | assert not sec2.next.next 56 | -------------------------------------------------------------------------------- /string/45_Autocomplete.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/3' 6 | 7 | Write an autocomplete class that returns all dictionary words with a given prefix. 8 | 9 | dict: {"abc", "acd", "bcd", "def", "a", "aba"} 10 | prefix: "a" -> "abc", "acd", "a", "aba" 11 | prefix: "b" -> "bcd" 12 | 13 | Notes: fetch all words recursively. 14 | ''' 15 | from collections import Counter 16 | 17 | 18 | class Trie(object): 19 | def __init__(self, words): 20 | self.d = {} 21 | self._build(words) 22 | 23 | def autocomplete(self, prefix): 24 | res = [] 25 | cur = self.d 26 | for char in prefix: 27 | cur = cur.get(char) 28 | if not cur: 29 | return res 30 | self._fetch(cur, res) 31 | return res 32 | 33 | def _build(self, words): 34 | for word in words: 35 | cur = self.d 36 | for char in word: 37 | cur = cur.setdefault(char, {}) 38 | cur['#'] = word 39 | 40 | def _fetch(self, cur, res): 41 | for k, v in cur.items(): 42 | if k == '#': 43 | res.append(v) 44 | else: 45 | self._fetch(v, res) 46 | 47 | 48 | if __name__ == '__main__': 49 | test = {"abc", "acd", "bcd", "def", "a", "aba"} 50 | trie = Trie(test) 51 | 52 | assert Counter(trie.autocomplete('a')) == Counter(["abc", "acd", "a", "aba"]) 53 | assert Counter(trie.autocomplete('b')) == Counter(['bcd']) 54 | assert Counter(trie.autocomplete('ab')) == Counter(['abc', 'aba']) 55 | -------------------------------------------------------------------------------- /array/04_Find-Duplicates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/24' 6 | 7 | Given an array of integers where each value 1 <= x <= len(array), write a function that finds all the duplicates in the array. 8 | 9 | dups([1, 2, 3]) = [] 10 | dups([1, 2, 2]) = [2] 11 | dups([3, 3, 3]) = [3] 12 | dups([2, 1, 2, 1]) = [1, 2] 13 | 14 | Notes: result no need in same order, revert the original array if in-place modification not allowed. 15 | ''' 16 | from collections import Counter 17 | 18 | 19 | def dups1(nums): 20 | # hashmap: O(n) time, O(n) space 21 | d = {} 22 | for num in nums: 23 | d[num] = d.get(num, 0) + 1 24 | return [k for k, v in d.items() if v > 1] 25 | 26 | 27 | def dups2(nums): 28 | # counting array: O(n) time, O(n) space 29 | count = [0 for _ in range(len(nums)+1)] 30 | res = [] 31 | for num in nums: 32 | if count[num] == 1: 33 | res.append(num) 34 | count[num] += 1 35 | return res 36 | 37 | 38 | def dups3(nums): 39 | # mapping base on '1 <= x <= len(nums)': O(n) time, O(1) space 40 | res = set() 41 | for i in range(len(nums)): 42 | sign = abs(nums[i]) - 1 43 | if nums[sign] > 0: 44 | nums[sign] = -nums[sign] 45 | else: 46 | res.add(abs(nums[i])) 47 | return list(res) 48 | 49 | 50 | if __name__ == '__main__': 51 | for dups in [dups1, dups2, dups3]: 52 | assert dups([1, 2, 3]) == [] 53 | assert dups([1, 2, 2]) == [2] 54 | assert dups([3, 3, 3]) == [3] 55 | assert Counter(dups([2, 1, 2, 1])) == Counter([1, 2]) 56 | -------------------------------------------------------------------------------- /recursion/21_Tree2Doubly-Linked-List.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/13' 6 | 7 | Given a tree, write a function to convert it into a circular doubly linked list from left to right by only modifying the existing pointers. 8 | ''' 9 | 10 | 11 | class TreeNode(object): 12 | def __init__(self, val, left=None, right=None): 13 | self.val = val 14 | self.left = left 15 | self.right = right 16 | 17 | 18 | class DllNode(object): 19 | def __init__(self, val, prev=None, next=None): 20 | self.val = val 21 | self.prev = prev 22 | self.next = next 23 | 24 | 25 | def convert(root): 26 | # InOrder + DllNode 27 | head, pre = None, None 28 | stack = [] 29 | while root or stack: 30 | if root: 31 | stack.append(root) 32 | root = root.left 33 | else: 34 | node = stack.pop() 35 | cur = DllNode(node.val) 36 | cur.prev = pre 37 | if pre: 38 | pre.next = cur 39 | pre = cur 40 | if not head: 41 | head = cur 42 | root = node.right 43 | return head 44 | 45 | 46 | if __name__ == '__main__': 47 | test = TreeNode(4) 48 | test.left = TreeNode(2) 49 | test.right = TreeNode(6) 50 | test.left.left = TreeNode(1) 51 | test.left.right = TreeNode(3) 52 | test.right.left = TreeNode(5) 53 | test.right.right = TreeNode(7) 54 | 55 | res = convert(test) 56 | outs = [] 57 | while res: 58 | outs.append(res.val) 59 | res = res.next 60 | assert outs == [1, 2, 3, 4, 5, 6, 7] 61 | -------------------------------------------------------------------------------- /stack/30_Palindromes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Given a linked list, write a function to determine whether the list is a palindrome. 8 | 9 | palindrome(1 -> 2 -> 3) = false 10 | palindrome(1 -> 2 -> 1) = true 11 | ''' 12 | 13 | 14 | class LinkNode(object): 15 | def __init__(self, val, next=None): 16 | self.val = val 17 | self.next = next 18 | 19 | 20 | def is_palindrome1(root): 21 | # two-pass 22 | dup, stack = root, [] 23 | while dup: 24 | stack.append(dup.val) 25 | dup = dup.next 26 | while root: 27 | if root.val != stack.pop(): 28 | return False 29 | root = root.next 30 | return True 31 | 32 | 33 | def is_palindrome2(root): 34 | # one-pass: slow and fast 35 | slow, fast, stack = root, root, [] 36 | while fast and fast.next: 37 | stack.append(slow.val) 38 | slow = slow.next 39 | fast = fast.next.next 40 | if fast: 41 | slow = slow.next 42 | while slow: 43 | if slow.val != stack.pop(): 44 | return False 45 | slow = slow.next 46 | return True 47 | 48 | 49 | if __name__ == '__main__': 50 | test1 = LinkNode(1) 51 | test1.next = LinkNode(2) 52 | test1.next.next = LinkNode(3) 53 | test2 = LinkNode(1) 54 | test2.next = LinkNode(2) 55 | test2.next.next = LinkNode(1) 56 | test3 = LinkNode(1) 57 | test3.next = LinkNode(2) 58 | test3.next.next = LinkNode(2) 59 | test3.next.next.next = LinkNode(1) 60 | 61 | for is_palindrome in [is_palindrome1, is_palindrome2]: 62 | assert not is_palindrome(test1) 63 | assert is_palindrome(test2) 64 | assert is_palindrome(test3) 65 | -------------------------------------------------------------------------------- /linked_list/43_Three-Sum.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/29' 6 | 7 | Given a list of integers, write a function that returns all sets of 3 numbers in the list, a, b, and c, so that a + b + c == 0. 8 | 9 | threeSum({-1, 0, 1, 2, -1, -4}) 10 | [-1, -1, 2] 11 | [-1, 0, 1] 12 | ''' 13 | 14 | 15 | def three_sum1(nums): 16 | # sort and clamp (Two Sum) 17 | nums.sort() 18 | res = [] 19 | for fir in range(len(nums)-2): 20 | if fir == 0 or nums[fir] > nums[fir-1]: 21 | sec, thi = fir+1, len(nums)-1 22 | while sec < thi: 23 | cur = [nums[fir], nums[sec], nums[thi]] 24 | if sum(cur) == 0: 25 | res.append(cur) 26 | if sum(cur) < 0: 27 | pre = sec 28 | while nums[sec] == nums[pre] and sec < thi: 29 | sec += 1 30 | else: 31 | pre = thi 32 | while nums[thi] == nums[pre] and sec < thi: 33 | thi -= 1 34 | return res 35 | 36 | 37 | solutions = [] 38 | 39 | 40 | def three_sum2(nums): 41 | # backtracking 42 | _dfs(nums, 0, 0, []) 43 | return sorted(solutions) 44 | 45 | 46 | def _dfs(nums, pos, target, tmp): 47 | if len(tmp) == 3: 48 | tmp.sort() 49 | if target == 0 and tmp not in solutions: 50 | solutions.append(tmp) 51 | return 52 | for i in range(pos, len(nums)): 53 | _dfs(nums, i+1, target-nums[i], tmp+[nums[i]]) 54 | 55 | 56 | if __name__ == '__main__': 57 | test = [-1, 0, 1, 2, -1, -4] 58 | 59 | for three_sum in [three_sum1, three_sum2]: 60 | assert three_sum(test) == [[-1, -1, 2], [-1, 0, 1]] 61 | -------------------------------------------------------------------------------- /array/10_Merge-Arrays.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/25' 6 | 7 | Given 2 sorted arrays, A and B, where A is long enough to hold the contents of A and B, write a function to copy the contents of B into A without using any buffer or additional memory. 8 | 9 | A = {1,3,5,0,0,0} 10 | B = {2,4,6} 11 | mergeArrays(A, B) 12 | A = {1,2,3,4,5,6} 13 | 14 | Notes: in-place modification 15 | ''' 16 | 17 | 18 | def merge1(arr1, arr2): 19 | # bottom-up swap 20 | m, n = len(arr1), len(arr2) 21 | if not m or m < n: 22 | return 23 | last = 0 24 | while arr1[last]: 25 | last += 1 26 | i, j = 0, 0 27 | while i < m and j < n: 28 | if arr1[i] > arr2[j]: 29 | for x in range(last, i, -1): 30 | arr1[x], arr1[x-1] = arr1[x-1], arr1[x] 31 | arr1[i] = arr2[j] 32 | j, last = j+1, last+1 33 | i += 1 34 | while j < n: 35 | arr1[last] = arr2[j] 36 | j, last = j+1, last+1 37 | 38 | 39 | def merge2(arr1, arr2): 40 | # top-down merge 41 | m, n = len(arr1), len(arr2) 42 | if not m or m < n: 43 | return 44 | length = 0 45 | while arr1[length]: 46 | length += 1 47 | i, j = length-1, n-1 48 | while i >= 0 and j >= 0: 49 | if arr1[i] > arr2[j]: 50 | arr1[i+j+1] = arr1[i] 51 | i -= 1 52 | else: 53 | arr1[i+j+1] = arr2[j] 54 | j -= 1 55 | while j >= 0: 56 | arr1[j] = arr2[j] 57 | j -= 1 58 | 59 | 60 | if __name__ == '__main__': 61 | A = [3, 5, 0, 0, 0, 0, 0, 0] 62 | B = [1, 2, 4, 6, 7] 63 | 64 | for merge in [merge1, merge2]: 65 | nums1, nums2 = A[:], B[:] 66 | merge(nums1, nums2) 67 | assert nums1 == [1, 2, 3, 4, 5, 6, 7, 0] 68 | -------------------------------------------------------------------------------- /array/11_Zero-Sum-Subarray.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Given an array, write a function to find any subarray that sums to zero, if one exists. 8 | 9 | zeroSum({1, 2, -5, 1, 2, -1}) = [2, -5, 1, 2] 10 | 11 | Notes: subarray means continuous range slice. 12 | ''' 13 | 14 | 15 | def zeroSum(arr): 16 | # hashmap: {pre_sum: pre} 17 | d, pre_sum, n = {}, 0, len(arr) 18 | for i in range(n+1): 19 | pre = d.get(pre_sum, -1) 20 | if pre != -1: 21 | return arr[pre:i] 22 | else: 23 | if i == n: 24 | break 25 | else: 26 | d[pre_sum] = i 27 | pre_sum += arr[i] 28 | return None 29 | 30 | 31 | solutions = [] 32 | solution = [] 33 | 34 | 35 | def find_one(arr): 36 | _dfs1(arr, 0, 0) 37 | return solution 38 | 39 | 40 | def _dfs1(arr, target, pos): 41 | if solution and target == 0: 42 | return True 43 | for i in range(pos, len(arr)): 44 | solution.append(arr[i]) 45 | if _dfs1(arr, target-arr[i], i+1): 46 | return True 47 | solution.pop() 48 | return False 49 | 50 | 51 | def find_all(arr): 52 | _dfs2(arr, 0, 0, []) 53 | return solutions 54 | 55 | 56 | def _dfs2(arr, target, pos, tmp): 57 | if tmp and target == 0: 58 | return solutions.append(tmp) 59 | for i in range(pos, len(arr)): 60 | _dfs2(arr, target-arr[i], i+1, tmp+[arr[i]]) 61 | 62 | 63 | if __name__ == '__main__': 64 | test = [1, 2, -5, 1, 2, -1] 65 | 66 | assert zeroSum([0]) == [0] 67 | assert zeroSum([0, 0, 0]) == [0] 68 | assert zeroSum([1, 2, 3, 0]) == [0] 69 | assert not zeroSum([1, 2, 3]) 70 | assert zeroSum(test) == [2, -5, 1, 2] 71 | 72 | assert find_one(test) in find_all(test) 73 | -------------------------------------------------------------------------------- /array/09_Matrix-Search.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/25' 6 | 7 | Given an n x m array where all rows and columns are in sorted order, write a function to determine whether the array contains an element x. 8 | 9 | contains([[1, 2, 3, 4] 10 | [5, 6, 7, 8] 11 | [9, 10, 11, 12]], 4) = True 12 | ''' 13 | 14 | 15 | def contains1(matrix, x): 16 | # binary search: land the row, then the col 17 | if not matrix or not matrix[0]: 18 | return False 19 | m, n = len(matrix), len(matrix[0]) 20 | lasts = [row[-1] for row in matrix] 21 | i = _search(lasts, x) 22 | if i == m: 23 | return False 24 | j = _search(matrix[i], x) 25 | if j == n: 26 | return False 27 | if x == matrix[i][j]: 28 | return True 29 | return False 30 | 31 | 32 | def _search(arr, x): 33 | lo, hi = 0, len(arr) - 1 34 | while lo <= hi: 35 | mid = (lo + hi) >> 1 36 | if x <= arr[mid]: 37 | hi = mid - 1 38 | else: 39 | lo = mid + 1 40 | return lo 41 | 42 | 43 | def contains2(matrix, x): 44 | # brute but concise 45 | if not matrix or not matrix[0]: 46 | return False 47 | m, n = len(matrix), len(matrix[0]) 48 | i, j = 0, n - 1 49 | while i < m and j >= 0: 50 | if x == matrix[i][j]: 51 | return True 52 | if x < matrix[i][j]: 53 | j -= 1 54 | else: 55 | i += 1 56 | return False 57 | 58 | 59 | if __name__ == '__main__': 60 | A = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]] 61 | 62 | for contains in [contains1, contains2]: 63 | assert contains(A, 4) 64 | assert contains(A, 6) 65 | assert contains(A, 9) 66 | assert not contains(A, 0) 67 | assert not contains(A, 13) 68 | -------------------------------------------------------------------------------- /stack/31_Max-Stacks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Implement a LIFO stack that has a push(), pop(), and max() function, where max() returns the maximum value in the stack. All of these functions should run in O(1) time. 8 | ''' 9 | 10 | 11 | class Node(object): 12 | def __init__(self, val, next=None, pre_max=None): 13 | self.val = val 14 | self.next = next 15 | self.pre_max = pre_max 16 | 17 | 18 | class MaxStack(object): 19 | ''' 20 | LinkNode: add/remove in O(1) time 21 | ''' 22 | def __init__(self): 23 | self.stack = None 24 | self.cur_max = None 25 | 26 | def push(self, x): 27 | p = Node(x) 28 | p.next = self.stack 29 | # self.stack == head 30 | self.stack = p 31 | 32 | p.pre_max = self.cur_max 33 | if not self.cur_max or p.val > self.cur_max.val: 34 | self.cur_max = p 35 | 36 | def pop(self): 37 | if not self.stack: 38 | raise ValueError 39 | 40 | head = self.stack 41 | self.stack = head.next 42 | self.cur_max = head.pre_max 43 | return head.val 44 | 45 | def max(self): 46 | if not self.cur_max: 47 | raise ValueError 48 | 49 | return self.cur_max.val 50 | 51 | 52 | if __name__ == '__main__': 53 | test = MaxStack() 54 | 55 | test.push(1) 56 | assert test.max() == 1 57 | test.push(2) 58 | assert test.max() == 2 59 | test.push(1) 60 | assert test.max() == 2 61 | assert test.pop() == 1 62 | assert test.max() == 2 63 | assert test.pop() == 2 64 | assert test.max() == 1 65 | assert test.pop() == 1 66 | 67 | try: 68 | test.max() 69 | except Exception as e: 70 | assert type(e) == ValueError 71 | 72 | try: 73 | test.pop() 74 | except Exception as e: 75 | assert type(e) == ValueError 76 | -------------------------------------------------------------------------------- /array/07_Square-Submatrix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/24' 6 | 7 | Given a 2D array of 1s and 0s, find the largest square subarray of all 1s. 8 | 9 | subarray([1, 1, 1, 0] 10 | [1, 1, 1, 1] 11 | [1, 1, 0, 0]) = 2 12 | ''' 13 | 14 | 15 | def subarray1(matrix): 16 | # brute 17 | if not matrix or not matrix[0]: 18 | return 0 19 | m, n = len(matrix), len(matrix[0]) 20 | res = 0 21 | for i in range(m): 22 | for j in range(n): 23 | res = max(res, _extend(matrix, i, j, m, n)) 24 | return res 25 | 26 | 27 | def _extend(matrix, i, j, m, n): 28 | length = 0 29 | while length < min(m-i, n-j): 30 | flag = True 31 | for x in range(i, i+1+length): 32 | if not matrix[x][j+length]: 33 | flag = False 34 | break 35 | for y in range(j, j+1+length): 36 | if not matrix[i+length][y]: 37 | flag = False 38 | break 39 | if flag: 40 | length += 1 41 | else: 42 | break 43 | return length 44 | 45 | 46 | def subarray2(matrix): 47 | # bottom-up dp: extend up and left 48 | if not matrix or not matrix[0]: 49 | return 0 50 | m, n = len(matrix), len(matrix[0]) 51 | res = 0 52 | dp = [[0 for _ in range(n)] for _ in range(m)] 53 | for i in range(m): 54 | for j in range(n): 55 | if not i or not j: 56 | dp[i][j] = 1 if matrix[i][j] else 0 57 | elif matrix[i][j]: 58 | dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1 59 | res = max(res, dp[i][j]) 60 | return res 61 | 62 | 63 | if __name__ == '__main__': 64 | matrix1 = [[1, 1, 1, 0], [1, 1, 1, 1], [1, 1, 0, 0]] 65 | matrix2 = [[1, 1, 1, 0], [1, 1, 1, 1], [1, 1, 1, 0]] 66 | 67 | for subarray in [subarray1, subarray2]: 68 | assert subarray(matrix1) == 2 69 | assert subarray(matrix2) == 3 70 | -------------------------------------------------------------------------------- /string/50_Priority-Queue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/2/12' 6 | 7 | Implement a Priority Queue 8 | ''' 9 | 10 | 11 | class PriorityQueue(object): 12 | ''' 13 | lowest first: min heap 14 | ''' 15 | def __init__(self): 16 | self._queue = [] 17 | 18 | def size(self): 19 | return len(self._queue) 20 | 21 | def push(self, item, priority): 22 | self._queue.append((item, priority)) 23 | self._shiftup(self.size()-1) 24 | 25 | def pop(self): 26 | last = self._queue.pop() 27 | if self._queue: 28 | top = self._queue[0] 29 | self._queue[0] = last 30 | self._shiftdown(0) 31 | return top[0] 32 | return last[0] 33 | 34 | def _shiftdown(self, pos): 35 | item, prior = self._queue[pos] 36 | childpos = (pos << 1) + 1 37 | while childpos < self.size(): 38 | if childpos < self.size()-1 and self._queue[childpos+1][1] < self._queue[childpos][1]: 39 | childpos += 1 40 | if prior <= self._queue[childpos][1]: 41 | break 42 | self._queue[pos] = self._queue[childpos] 43 | pos = childpos 44 | childpos = (pos << 1) + 1 45 | self._queue[pos] = (item, prior) 46 | 47 | def _shiftup(self, pos): 48 | item, prior = self._queue[pos] 49 | parentpos = (pos - 1) >> 1 50 | while parentpos >= 0: 51 | if prior >= self._queue[parentpos][1]: 52 | break 53 | self._queue[pos] = self._queue[parentpos] 54 | pos = parentpos 55 | parentpos = (pos - 1) >> 1 56 | self._queue[pos] = (item, prior) 57 | 58 | 59 | if __name__ == '__main__': 60 | test = PriorityQueue() 61 | test.push('fourth', 4) 62 | test.push('first', 1) 63 | test.push('second', 2) 64 | test.push('third', 3) 65 | 66 | assert test.size() == 4 67 | assert test.pop() == 'first' 68 | assert test.size() == 3 69 | assert test.pop() == 'second' 70 | assert test.pop() == 'third' 71 | assert test.pop() == 'fourth' 72 | -------------------------------------------------------------------------------- /array/13_N-Stacks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding=utf-8 3 | ''' 4 | __author__ = 'sunp' 5 | __date__ = '2019/1/26' 6 | 7 | Implement N > 0 stacks using a single array to store all stack data (you may use auxiliary arrays in your stack object, but all of the objects in all of the stacks must be in the same array). No stack should be full unless the entire array is full. 8 | 9 | N = 3; 10 | capacity = 10; 11 | Stacks stacks = new Stacks(N, capacity); 12 | stacks.push(0, 10); 13 | stacks.push(2, 11); 14 | stacks.pop(0) = 10; 15 | stacks.pop(2) = 11; 16 | ''' 17 | 18 | 19 | class Stacks2(object): 20 | def __init__(self, capacity): 21 | self.cap = capacity 22 | self.top1 = -1 23 | self.top2 = capacity 24 | self.store = [None] * capacity 25 | 26 | def push(self, id, num): 27 | if self.top1 == self.top2 - 1: 28 | raise OverflowError('Stack Overflow!') 29 | 30 | if id == 1: 31 | self.top1 += 1 32 | self.store[self.top1] = num 33 | else: 34 | self.top2 -= 1 35 | self.store[self.top2] = num 36 | 37 | def pop(self, id): 38 | # no need to remove self.store[top] 39 | if (id == 1 and self.top1 == -1) or (id == 2 and self.top2 == self.cap): 40 | raise OverflowError('Stack Underflow!') 41 | 42 | if id == 1: 43 | num = self.store[self.top1] 44 | self.top1 -= 1 45 | return num 46 | else: 47 | num = self.store[self.top2] 48 | self.top2 += 1 49 | return num 50 | 51 | 52 | class StacksK(object): 53 | def __init__(self, N, capacity): 54 | self.k = N 55 | self.cap = capacity 56 | self.avail = 0 57 | self.tops = [-1 for _ in range(N)] 58 | self.avails = [i+1 for i in range(capacity-1)] 59 | self.avails.append(-1) 60 | self.store = [None] * capacity 61 | 62 | def push(self, id, num): 63 | if id not in range(self.k): 64 | raise IndexError 65 | if self.avail == -1: 66 | raise OverflowError('Stack Overflow!') 67 | 68 | cur = self.avail 69 | self.store[cur] = num 70 | self.avail = self.avails[cur] 71 | self.avails[cur] = self.tops[id] 72 | self.tops[id] = cur 73 | 74 | def pop(self, id): 75 | # reverse push 76 | if id not in range(self.k): 77 | raise IndexError 78 | if self.tops[id] == -1: 79 | raise OverflowError('Stack Underflow!') 80 | 81 | top = self.tops[id] 82 | self.tops[id] = self.avails[top] 83 | self.avails[top] = self.avail 84 | self.avail = top 85 | return self.store[top] 86 | 87 | 88 | if __name__ == '__main__': 89 | test = StacksK(3, 10) 90 | test.push(0, 10) 91 | test.push(2, 11) 92 | 93 | assert test.pop(0) == 10 94 | assert test.pop(2) == 11 95 | 96 | try: 97 | test.pop(1) 98 | except Exception as e: 99 | assert type(e) == OverflowError 100 | assert str(e) == 'Stack Underflow!' 101 | else: 102 | print('OverflowError not raised.') 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 50-coding-interview-questions 2 | > Don't do another coding interview... Until you've mastered these 50 whiteboarding questions. 3 | >
—Sam Gavis-Hughson, founder of Byte by Byte
4 | 5 | You can subscribe these problems from [here](https://www.byte-by-byte.com/50-questions/). Really appreciate it to [Sam](https://www.byte-by-byte.com/about/). 6 | 7 | ## Guide 8 | Learn how to develop a systematic approach to each problem as follows: 9 | 10 | ### 1. Understand the problem 11 | Understanding exactly what is being asked is critical to your success. Ask any clarifying questions necessary. Look at the input and figure out how that resulted in the given output. If you see any obvious problem areas or edge cases, add in your own example. Based on the input and output, what should be the signature of your function? 12 | 13 | ### 2. Find a brute force solution 14 | The brute force solution makes you really understand the problem, and guarantees that you find a solution. Far too many people try to jump into an optimized solution and get lost. It's really hard to recover without simply starting over, and in an interview you definitely don't have time to start over. 15 | 16 | ### 3. Optimize the brute force solution 17 | Time to shine yourself. But you shouldn't spend more than 5-10 minutes on this step before moving on to coding. Try brainstorming more efficient data structures, looking at duplicated or unnecessary work, or just looking for more efficient solutions unrelated to your brute force solution. 18 | 19 | ### 4. Code the solution 20 | Since you've done all the legwork, coding should be the easy part. If not, it's worth dedicating time to practicing coding, particularly on paper or a whiteboard. 21 | 22 | ### 5. Test the solution 23 | This final step is critical. Not only does it show your interviewer that you're careful and thorough, it gives yourself the confidence that your solution is correct. Go through the code line by line. Missing small typos or indexing errors could leave a bad taste in your interviewer's mouth even if you did well otherwise. 24 | 25 | ## Problems 26 | ### Array(1-14) 27 | - [x] [Median of Arrays](array/01_Median-of-Arrays.py) 28 | - [x] [0-1 Knapsack](array/02_0-1-Knapsack.py) 29 | - [x] [Matrix Product](array/03_Matrix-Product.py) 30 | - [x] [Find Duplicates](array/04_Find-Duplicates.py) 31 | - [x] [Consecutive Array](array/05_Consecutive-Array.py) 32 | - [x] [Zero Matrix](array/06_Zero-Matrix.py) 33 | - [x] [*Square Submatrix](array/07_Square-Submatrix.py) 34 | - [x] [Merge K Arrays](array/08_Merge-K-Arrays.py) 35 | - [x] [Matrix Search](array/09_Matrix-Search.py) 36 | - [x] [Merge Arrays](array/10_Merge-Arrays.py) 37 | - [x] [Zero Sum Subarray](array/11_Zero-Sum-Subarray.py) 38 | - [x] [Permutations](array/12_Permutations.py) 39 | - [x] [**N Stacks](array/13_N-Stacks.py) 40 | - [x] [Anagrams](array/14_Anagrams.py) 41 | 42 | ### Graph(15-16) 43 | - [x] [Build Order](graph/15_Build-Order.py) 44 | - [ ] [Shortest Path](graph/16_Shortest-Path.py) 45 | 46 | ### Recursion(17-26) 47 | - [x] [Random Binary Tree](recursion/17_Random-Binary-Tree.py) 48 | - [x] [Lowest Common Ancestor](recursion/18_Lowest-Common-Ancestor.py) 49 | - [x] [Sum](recursion/19_Sum.py) 50 | - [x] [*Reverse Stack](recursion/20_Reverse-Stack.py) 51 | - [x] [Tree to Doubly Linked List](recursion/21_Tree2Doubly-Linked-List.py) 52 | - [x] [Longest Consecutive Branch](recursion/22_Longest-Consecutive-Branch.py) 53 | - [x] [Print Reversed Linked List](recursion/23_Print-Reversed-Linked-List.py) 54 | - [x] [Balanced Binary Tree](recursion/24_Balanced-Binary-Tree.py) 55 | - [x] [Binary Search Tree Verification](recursion/25_BST-Verification.py) 56 | - [x] [**Smallest Change](recursion/26_Smallest-Change.py) 57 | 58 | ### Stack(27-31) 59 | - [x] [Inorder Traversal](stack/27_Inorder-Traversal.py) 60 | - [x] [Sort Stacks](stack/28_Sort-Stacks.py) 61 | - [x] [Stack from Queues](stack/29_Stack-from-Queues.py) 62 | - [x] [Palindromes](stack/30_Palindromes.py) 63 | - [x] [**Max Stacks](stack/31_Max-Stacks.py) 64 | 65 | ### Bit Manipulation(32-37) 66 | - [x] [Two Missing Numbers](bit/32_Two-Missing-Numbers.py) 67 | - [x] [Bit Int Modulus](bit/33_Bit-Int-Modulus.py) 68 | - [x] [Swap Variables](bit/34_Swap-Variables.py) 69 | - [x] [Gray Code](bit/35_Gray-Code.py) 70 | - [x] [Rotate Bits](bit/36_Rotate-Bits.py) 71 | - [x] [Number of Ones in a Binary Number](bit/37_Ones-in-Binary.py) 72 | 73 | ### Linked List(38-44) 74 | - [x] [Linked List Cycles](linked_list/38_Linked-List-Cycles.py) 75 | - [x] [Random Linked List](linked_list/39_Random-Linked-List.py) 76 | - [x] [Dedup Linked List](linked_list/40_Dedup-Linked-List.py) 77 | - [x] [Split a Linked List](linked_list/41_Split-Linked-List.py) 78 | - [x] [Nth to the Last Element](linked_list/42_Nth-Last-Element.py) 79 | - [x] [Three Sum](linked_list/43_Three-Sum.py) 80 | - [x] [Tree Level Order](linked_list/44_Tree-Level-Order.py) 81 | 82 | ### String(45-51) 83 | - [x] [Autocomplete](string/45_Autocomplete.py) 84 | - [x] [String Deletion](string/46_String-Deletion.py) 85 | - [x] [*Longest Common Substring](string/47_Longest-Common-Substring.py) 86 | - [x] [String Compression](string/48_String-Compression.py) 87 | - [x] [Fibonacci Number](string/49_Fibonacci-Number.py) 88 | - [x] [**Priority Queue](string/50_Priority-Queue.py) 89 | - [x] [Kth Most Frequent String](string/51_Kth-Most-Frequent-String.py) 90 | --------------------------------------------------------------------------------