The selection sort algorithm sorts an array by repeatedly finding the minimum element from unsorted part and putting it at the beginning.
Time : O(n^2), as there are two nested loops
Space : O(1) (In-place)
1 2 3 4 5 6 7 8
defselectionSort(nums): n = len(nums) for i in xrange(n): minIndex = i for j in xrange(i, n): if nums[j] < nums[minIndex]: minIndex = j nums[i], nums[minIndex] = nums[minIndex], nums[i]
Bubble Sort
Bubble Sort is the simplest sorting algorithm that works by repeatedly swapping the adjacent elements if they are in wrong order.
Time : O(n^2), Best Case : O(n)
Space : O(1)
1 2 3 4 5 6
defbubbleSort(nums): for i in xrange(len(nums)): for j in xrange(len(nums)-i-1): if nums[j] > nums[j+1]: nums[j+1], nums[j] = nums[j], nums[j+1] return nums
Insertion Sort
Time : O(n^2), Best Case : O(n) when the elements are already sorted!
Space : O(1)
1 2 3 4 5 6 7 8
definsertionSort(nums): for i, num in enumerate(nums): j = i while j > 0and nums[j-1] > num: nums[j] = nums[j-1] j -= 1 nums[j] = num return nums
Merge sort
It divides input array in two halves, calls itself for the two halves and then merges the two sorted halves.
if very large,so data in disk : read and write logk times
sol3: k pointers smallest element(Heap)
if very large,so data in disk : read and write only 1 time
Counting Inversions
based on merge sort, count all the inversion paris!
Quick Sort
It picks an element as pivot and partitions the given array around the picked pivot.
The key process in quickSort is partition(). Target of partitions is, given an array and an element x of array as pivot, put x at its correct position in sorted array and put all smaller elements (smaller than x) before x, and put all greater elements (greater than x) after x. All this should be done in linear time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
defpartition(nums, l, r): pivot = nums[r] # pick last element as pivot low = l for i in xrange(l, r): if nums[i] < pivot: nums[i], nums[low] = nums[low], nums[i] low += 1 nums[low], nums[r] = nums[r], nums[low] return low
classSolution: """ @param nums: A list of integers @return: An integer denotes the middle number of the array """ defmedian(self, nums): n = len(nums) k = (n + 1) // 2 - 1# index we want to find left, right = 0, len(nums)-1 while left <= right: idx = self.partition(nums, left, right) if idx == k: break elif idx < k: left = idx + 1 else: right = idx - 1
return nums[k]
defpartition(self, nums, left, right): pivot = nums[right] low = left for i in range(left, right): if nums[i] < pivot: nums[i], nums[low] = nums[low], nums[i] low += 1 nums[low], nums[right] = nums[right], nums[low] return low
Maintaining the relative order of the non-zero elements.
1 2 3 4 5 6 7 8 9 10
classSolution(object): defmoveZeroes(self, nums): non0 = 0 n = len(nums) for i in xrange(n): if nums[i] != 0: nums[non0] = nums[i] non0 += 1 for i in xrange(non0, n): nums[i] = 0
1 2 3 4 5 6 7 8
classSolution(object): defmoveZeroes(self, nums): non0 = 0 n = len(nums) for i in xrange(n): if nums[i] != 0: nums[non0], nums[i] = nums[i], nums[non0] non0 += 1
Follow up : order can be changed
The order of all other elments can be changed.
left and right pointer swap each number, it could change the order, but it reduces the operation number!
this method actually is similar to the partition function in Quick Sort!!!
classSolution(object): defmoveZeroes(self, nums): left, right = 0, len(nums)-1 while left < right: while left < right and nums[left] != 0: left += 1 while left < right and nums[right] == 0: right -= 1 nums[left], nums[right] = nums[right], nums[left] print("operation") left += 1 right -= 1
# Simplified Version classSolution(object): defmoveZeroes(self, nums): left, right = 0, len(nums)-1 while left < right: if nums[left] != 0: left += 1 elif nums[right] == 0: right -= 1 else: nums[left], nums[right] = nums[right], nums[left] left += 1 right -= 1
# Time : O(n) # Space : O(n) classSolution(object): defsortColors(self, nums): cnt = collections.defaultdict(int) for num in nums: cnt[num] += 1
p = 0 for i in xrange(3): for _ in xrange(cnt[i]): nums[p] = i p += 1
Sol2. Dutch Partitioning Problem
one-pass algorithm
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution(object): defsortColors(self, nums): l, r = 0, len(nums)-1 i = 0 while i <= r: if nums[i] == 0: nums[l], nums[i] = nums[i], nums[l] l += 1 i += 1# missing part elif nums[i] == 1: i += 1 else: nums[i], nums[r] = nums[r], nums[i] r -= 1
classSolution(object): defwiggleSort(self, nums): nums.sort() for i in range(2, len(nums), 2): nums[i], nums[i-1] = nums[i-1], nums[i]
Solution2. One Pass
Time : O(n)
1 2 3 4 5
classSolution(object): defwiggleSort(self, nums): for i in range(1, len(nums)): if (i % 2 == 0and nums[i] > nums[i-1]) or (i % 2 == 1and nums[i] < nums[i-1]): nums[i], nums[i-1] = nums[i-1], nums[i]
classSolution(object): defwiggleSort(self, nums): temp = sorted(nums) n = len(nums) for i in range(1, n, 2): nums[i] = temp.pop() for i in range(0, n, 2): nums[i] = temp.pop()
Big data
Sort with small memory
Given a single computer with a single CPU and a single core, which has 2GB of memory and 1GB available for use, it also has two 100GB hard drives. How to sort 80GB integers of 64bits.?
External Sort
400 chunks. Use 1GB memory to sort each 0.2GB. => write to disk
400-way merge chunk1 xxxxxx xxxxxxxxxxxxxxx chunk2 yyyyyy yyyyyyyyyyyyyyy … chunk400 zzzzzz zzzzzzzzzzzzzzz read from each chunk, block by block, insert into a min Heap.
result block
Why read one block by block instead of one by one? File IO!!!
Find the minimal sum cost from root to leaf in a Tree (Not necessarily binary)
1 2 3 4 5 6 7 8 9
# Time : O(n), Space : O(n) defget_cheapest_cost(rootNode): ifnot rootNode.children: return rootNode.cost
minCost = float('inf') for child in rootNode.children: minCost = min(minCost, get_cheapest_cost(child)) return minCost + rootNode.cost
follow up: TODO
BST Successor Search
In a Binary Search Tree (BST), an Inorder Successor of a node is defined as the node with the smallest key greater than the key of the input node (see examples below).
Given a node inputNode in a BST, you’re asked to write a function findInOrderSuccessor that returns the Inorder Successor of inputNode. If inputNode has no Inorder Successor, return null.
ifnot inputNode.right: # dose not have right subtree parent = inputNode.parent ifnot parent: # root node returnNone while parent: if parent.key > inputNode.key: return parent parent = parent.parent returnNone else:# have right subree cur = inputNode.right while cur.left: cur = cur.left return cur
Case1 : inputNode has a right child. In this case successorNode would be the node with the minimum key in inputNode’s right subtree.
Case2: inputNode doesn’t have a right child. In this case successorNode would be one of inputNode’s ancestors. More specifically, within inputNode’s ancestor chain (starting from inputNode all the way up to the root), successorNode is the first parent that has a left child in that chain.
Other
H-Tree Construction
Write a function drawHTree that constructs an H-tree, given its center (x and y coordinates), a starting length, and depth. Assume that the starting line is parallel to the X-axis.
Use the function drawLine provided to implement your algorithm. In a production code, a drawLine function would render a real line between two points. However, this is not a real production environment, so to make things easier, implement drawLine such that it simply prints its arguments (the print format is left to your discretion).
Analyze the time and space complexity of your algorithm. In your analysis, assume that drawLine’s time and space complexities are constant, i.e. O(1).
Given an array arr of unique nonnegative integers, implement a function getDifferentNumber that finds the smallest nonnegative integer that is NOT in the array.
1 2 3 4 5 6 7 8
# Time : O(n) # Space : O(n) defget_different_number(arr): n = len(arr) arr_set = set(arr) for i in xrange(n+1): if i notin arr_set: return i
Array Index & Element Equality
Given a sorted array arr of distinct integers, write a function indexEqualsValueSearch that returns the lowest index i for which arr[i] == i. Return -1 if there is no such index.
# First Reverse start = 0 for i in xrange(len(arr)+1): if i == len(arr) or arr[i] == ' ': # condition end = i - 1 while start < end: arr[start], arr[end] = arr[end], arr[start] start += 1 end -= 1 start = i + 1
# Second Reverse start, end = 0, len(arr)-1 while start < end: arr[start], arr[end] = arr[end], arr[start] start += 1 end -= 1 return arr
Matrix Spiral Copy
Given a 2D array (matrix) inputMatrix of integers, create a function spiralCopy that copies inputMatrix’s values into a 1D array in a spiral order, clockwise. Your function then should return that array. Analyze the time and space complexities of your solution.
m, n = len(matrix), len(matrix[0]) directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] k = 0 visited = set() x = y = 0 res = [] while len(res) < m * n: res.append(matrix[x][y]) visited.add((x, y))
dx, dy = directions[k] nx, ny = x+dx, y+dy if (nx, ny) notin visited and0 <= nx <= m-1and0 <= ny <= n-1: x, y = nx, ny else: # need to change direction k = (k + 1) % 4 dx, dy = directions[k] x, y = x+dx, y+dy return res
classSolution(object): defmaxProfit(self, prices): res = 0 for i in xrange(1, len(prices)): if prices[i] > prices[i-1]: res += prices[i]-prices[i-1] return res
classSolution(object): defmaxProfit(self, prices): n = len(prices) if n <= 0: return0
left = [0] * n minp = prices[0] for i in xrange(1, len(prices)): minp = min(minp, prices[i]) left[i] = max(left[i-1], prices[i] - minp)
maxp = prices[-1] right = [0] * (n) res = 0 for i in xrange(len(prices)-2, -1, -1): maxp = max(maxp, prices[i]) right[i] = max(maxp - prices[i], right[i+1]) res = max(res, right[i] + (left[i-1] if i > 1else0)) return res
classSolution: defmaxProfit(self, k, prices): ifnot prices or k == 0: return0
n = len(prices) if k > n // 2: return self.helper(prices)
glob = [[0] * (k+1) for _ in range(n)] local = [[0] * (k+1) for _ in range(n)] for i in range(1, n): diff = prices[i] - prices[i-1] for j in range(1, k+1): local[i][j] = max(glob[i-1][j-1], local[i-1][j]) + diff glob[i][j] = max(glob[i-1][j], local[i][j]) return glob[-1][-1]
defhelper(self, prices): res = 0 for i in range(1, len(prices)): res += max(0, prices[i] - prices[i-1]) return res
classSolution: defmaxProfit(self, k, prices): ifnot prices or k == 0: return0
n = len(prices) if k > n // 2: return self.helper(prices)
hold = [[0] * (k+1) for _ in range(n)] unhold = [[0] * (k+1) for _ in range(n)]
hold[0][0] = -prices[0] for i in range(1, n): hold[i][0] = max(hold[i-1][0], -prices[i])
for i in range(k+1): hold[0][i] = -prices[0]
for i in range(1, n): for j in range(1, k+1): hold[i][j] = max(hold[i-1][j], unhold[i-1][j]-prices[i]) unhold[i][j] = max(unhold[i-1][j], hold[i-1][j-1]+prices[i]) return unhold[-1][-1]
n = len(prices) buy = [0] * n sell = [0] * n buy[0] = -prices[0] for i in range(1, n): buy[i] = max(buy[i-1], (sell[i-2] if i > 1else0) - prices[i]) sell[i] = max(sell[i-1], buy[i-1] + prices[i]) return sell[-1]
Binary Search Tree: for every single node in the tree, the values in its left subtree are all smaller than its value, and the values in its right subtree are all larger than its value.
if key > root.val: root.right = self.deleteNode(root.right, key) elif key < root.val: root.left = self.deleteNode(root.left, key) else: ifnot root.left andnot root.right: returnNone elifnot root.left ornot root.right: return root.left if root.left else root.right else: # Trick Point : swap the largest number in its left subree with current node # then we recursion delete the new val in its subtree cur = root.right while cur.left: cur = cur.left root.val = cur.val root.right = self.deleteNode(root.right, cur.val) return root
# Iteration Time : O(h), Space : O(1) classSolution(object): defsearchBST(self, root, val): cur = root while cur: if cur.val == val: return cur elif cur.val < val: cur = cur.right else: cur = cur.left returnNone
Binary Search Tree actually seperate ordered list to three part, less than root.val, root.val, more than root.val instead of two part!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution(object): defclosestValue(self, root, target): cur = root res = cur.val while cur: if abs(cur.val - target) < abs(res - target): res = cur.val
if cur.val == target: return cur.val elif cur.val < target: cur = cur.right else: cur = cur.left return res
classSolution(object): defclosestKValues(self, root, target, k): res = [] self.dfs(root, res) # Two Pointer Shrink left, right = 0, len(res)-1 while right - left + 1 > k: if abs(res[left] - target) < abs(res[right] - target): right -= 1 else: left += 1 return res[left:right+1]
If we use Two Stack to solve PostOrder Traversal, Problem “Inorder Traversal” is more complexity than other two traversal! Whether it is or not a binary search tree, the solutions are the same!
1 push element to the stack until its left node is none
2 pop a node from the stack, traverse its right subtree
Inorder Traversal Seperated two parts!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classBSTIterator(object): def__init__(self, root): cur = root self.stack = [] while cur: self.stack.append(cur) cur = cur.left
defhasNext(self): return len(self.stack) > 0
defnext(self): node = self.stack.pop() cur = node.right while cur: self.stack.append(cur) cur = cur.left return node.val
classSolution: deflongestCommonSubstring(self, A, B): m, n = len(A), len(B) dp = [[0] * (n+1) for _ in range(m+1)] res = 0 for i in range(m): for j in range(n): if A[i] == B[j]: dp[i+1][j+1] = dp[i][j] + 1 res = max(res, dp[i+1][j+1]) return res
State : dp[i][j] means Longest Common Subsequence for A[0…i-1] and B[0…j-1]
Init : dp[i][0] = dp[0][j] = 0
Deduction Formula :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# Time : O(mn), Space : O(mn) classSolution: deflongestCommonSubsequence(self, A, B): m, n = len(A), len(B) if m == 0or n == 0: return0
dp = [[0] * (n+1) for _ in xrange(m+1)] for i in xrange(1, m+1): for j in xrange(1, n+1): if A[i-1] == B[j-1]: dp[i][j] = dp[i-1][j-1] + 1 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) return dp[-1][-1]
Space Improvement dp[i][j] -> dp[i % 2][j]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# Space : O(n) classSolution: deflongestCommonSubsequence(self, A, B): m, n = len(A), len(B) if m == 0or n == 0: return0
dp = [[0] * (n+1) for _ in xrange(2)] for i in xrange(1, m+1): for j in xrange(1, n+1): if A[i-1] == B[j-1]: dp[i % 2][j] = dp[(i-1) % 2][j-1] + 1 else: dp[i % 2][j] = max(dp[(i-1) % 2][j], dp[i % 2][j-1]) return dp[m % 2][-1]
classSolution(object): defisMatch(self, s, p): m, n = len(s), len(p) dp = [[False] * (n+1) for _ in range(m+1)] dp[0][0] = True for i in range(n): if p[i] == "*": dp[0][i+1] = dp[0][i-1]
for i in range(m): for j in range(n): if s[i] == p[j] or p[j] == '.': dp[i+1][j+1] = dp[i][j] elif p[j] == '*': dp[i+1][j+1] = dp[i+1][j-1] if p[j-1] == '.'or p[j-1] == s[i]: dp[i+1][j+1] = dp[i+1][j+1] or dp[i][j+1] return dp[-1][-1]
classSolution(object): defisInterleave(self, s1, s2, s3): m, n, l = len(s1), len(s2), len(s3) if m + n != l: returnFalse
dp = [[False] * (n+1) for _ in range(m+1)] dp[0][0] = True for i in range(m): if s1[i] == s3[i]: dp[i+1][0] = dp[i][0]
for j in range(n): if s2[j] == s3[j]: dp[0][j+1] = dp[0][j]
for i in range(m): for j in range(n): if s3[i+j+1] == s1[i]: dp[i+1][j+1] = dp[i+1][j+1] or dp[i][j+1] if s3[i+j+1] == s2[j]: dp[i+1][j+1] = dp[i+1][j+1] or dp[i+1][j] return dp[-1][-1]
State swap[i]: The cost of making both sequences increasing up to the first i columns in condition that we swap A[i] and B[i] notswap: The cost of making both sequences increasing up to the first i columns in condition that we do not swap A[i] and B[i]
Init : swap[0] = 1; notswap[0] = 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution(object): defminSwap(self, A, B): n = len(A) swap, notswap = [1001] * n, [1001] * n swap[0] = 1 notswap[0] = 0
for i in range(1, n): # Case 1 : strictly increasing, swap 2 or not if A[i-1] < A[i] and B[i-1] < B[i]: swap[i] = swap[i-1] + 1 notswap[i] = notswap[i-1]
# Case 2 : swap position i-1 or swap position i if A[i-1] < B[i] and B[i-1] < A[i]: swap[i] = min(swap[i], notswap[i-1] + 1) notswap[i] = min(notswap[i], swap[i-1])
classSolution(object): deffindOrder(self, numCourses, prerequisites): indegree = [0] * numCourses graph = collections.defaultdict(list) for u, v in prerequisites: indegree[u] += 1 graph[v].append(u)
queue = collections.deque([i for i in xrange(numCourses) if indegree[i] == 0]) res = [] while queue: node = queue.popleft() res.append(node) for v in graph[node]: indegree[v] -= 1 if indegree[v] == 0: queue.append(v) return res if sum(indegree) == 0else []
classSolution(object): defalienOrder(self, words): indegree = {} for word in words: for c in word: indegree[c] = 0
graph = [set() for _ in xrange(26)] for i in xrange(len(words)-1): cur = words[i] next = words[i+1] for j in xrange(min(len(cur), len(next))): if cur[j] != next[j]: if next[j] notin graph[ord(cur[j])-ord('a')]: graph[ord(cur[j])-ord('a')].add(next[j]) indegree[next[j]] += 1 break
res = [] cnt = len(indegree.keys()) q = collections.deque([c for c in indegree.keys() if indegree[c] == 0]) while q: cur = q.popleft() res.append(cur) for n in list(graph[ord(cur)-ord('a')]): indegree[n] -= 1 if indegree[n] == 0: q.append(n) return''.join(res) if len(res) == cnt else""
Usage : store the local information for each recursion function
1 Look Like
function calls itself
2 In Fact
Boil down a big problem to smaller ones(size n depends on size n-1, or n-2 or … n/2); Boil down size=n problem to size=n-1,n-2orn/2 problem
3 How to Implementate
1)Base Case:smallest problem to solve
2)Recursive rule: how to make the problem smaller (if we can resolve the same problem but with a smaller size, then what is left to do for the current problem size n)
4 In more details
Recursive Function Signature must keep the same logic
Recursive rule: For the i-th queen on the i-th row, we must make sure the Qi does no conflict with all previous queens that have been placed on the board. int Position[N] = {1 2 3 5 …}
Base Case: The last row is done, 0 row left
Recursive rule: iff position(i,j)valid - >go to the next roe:(i+1) Time = O(88) -> O(8!)
2 How to print 2D array in spiral order (NxN) x x x x x x x x x x x x x x x x x x x x
Q2 A word such as “book” can be abbreviated to 4, “1o1k”, “b3”, “b2k”, etc. Given a string and an abbreviation, return if the string matches the abbreviation. Assume the original string only contains alphabetic characters.
5. Recursion和Tree的结合
Binary tree 往往是最常见的,和recursion结合最紧密的面试题目类型
树本身就是Recursive的结构!
每层的node具备的性质,传递的值和下一层的性质往往一致。比较容易定义recursive rule
Base case(generally):null pointer under the leaf node
Tree + Recursion 第一类问题:从下往上反值(int ,bool,etc.)
* 从下往上反值 - getHeight(Node root)
* 从下往上反值 - 统计tree里每个node的左子树有多少个node?
* 从下往上返值 - Find the node with **max difference** in the total number of descendents in its left subtree and right subtree
【Way of thinking】 (相当于后序遍历)
1.What do you expect from your lchild/rhild? (usually it is the return type of the recursion type)
2.What do you want to do in the current layer?
3.What do you want to report to your parent?(same as Q1 == Q3)
# Time : O(n), Space : O(n) classSolution(object): defhasCycle(self, head): visited = set() while head: if head in visited: returnTrue visited.add(head) head = head.next returnFalse
Sol2. 快慢指针
小trick : 如果有环,快指针一定会追上慢指针
1 2 3 4 5 6 7 8 9 10
# Time : O(n), Space : O(1) classSolution(object): defhasCycle(self, head): slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: returnTrue returnFalse
# Time : O(n), Space : O(n) classSolution(object): defdetectCycle(self, head): visited = set() while head: if head in visited: return head visited.add(head) head = head.next returnNone
Sol2. 快慢指针
小trick : 关于长度的证明,相遇点到交点的距离=起点到交点的距离!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# Time : O(n), Space : O(1) classSolution(object): defdetectCycle(self, head): ifnot head: returnNone fast = slow = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: # Detect Cycle fast = head while slow != fast: fast = fast.next slow = slow.next return fast returnNone
但是又要求 Time : O(n), Space : O(1), Input Array Read only,只能考虑其他解法!
(比较难想到)Array里index, value中把value当作”next”的index,就可以把问题转化成了Linked List II
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# Time : O(n), Space : O(1) classSolution(object): deffindDuplicate(self, nums): ifnot nums: return0 slow = fast = nums[0] # Start from index 0! whileTrue: slow = nums[slow] fast = nums[nums[fast]] if slow == fast: break fast = nums[0] while fast != slow: fast = nums[fast] slow = nums[slow] return fast