Review
- Recursion - 5*
- DFS - 5
- BFS - 5
- Dynamic Programming - 3
- Tree, Trie - 4
- Stack, Queue 4
What we have covered
- Heap 4
- HashMap - 4
- Sort - 3.5
- Bit Manipulation - 1
- Graph & Topology Sort - 4
- Union Find - 3
- Binary Search - 3.5
- Two Pointers - 3.5
- OO & System Design
Recursion & DFS
Find Sub-problems
Is it || or &&?
Watch out for the edge case and exit
Is Recursion the best way?
class DFSExample:
def dfs(self, node, visited=set()):
# Base case: return if node is already visited
if node in visited:
return
# Mark the node as visited
visited.add(node)
# Process the node (optional)
print("Visited:", node.value)
# Recursively visit all neighbors
for neighbor in node.neighbors:
self.dfs(neighbor, visited)
BFS
When to push items into queue?
Avoid null into queue
DFS for helping backtracking results
def levelOrder(root):
results = []
queue = []
if root is not None:
queue.append(root)
while len(queue) > 0:
oneResult = []
queue2 = []
while len(queue) > 0:
top = queue.pop(0)
if top.left is not None:
queue2.append(top.left)
if top.right is not None:
queue2.append(top.right)
oneResult.append(top.val)
results.append(oneResult)
queue = queue2
return results
Dynamic Programming
What is the sub-optimal structure?
Deduction equation
Space complexity
class FibonacciTabulation:
def fibonacci(self, n):
# Handle base cases
if n <= 1:
return n
# Initialize base values in the table
dp = [0] * (n + 1)
dp[1] = 1
# Fill the table from the bottom up
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
Tree & Trie
How to traverse
Remember the structure properties
Go with BFS/DFS
BST -> Balanced BST
def preorder(root):
if root is not None:
# Visit the node by printing the node data
print(root.data, end=" ")
preorder(root.left)
preorder(root.right)
def inorder(root):
if root is not None:
inorder(root.left)
print(root.data, end=" ")
inorder(root.right)
def postorder(root):
if root is not None:
postorder(root.left)
postorder(root.right)
print(root.data, end=" ")
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
stack, res = [], []
while stack or root:
while root:
stack.append(root) # first time
res.append(root.val)
root = root.left
if stack:
top = stack.pop()
if top.right:
root = top.right
return res def inorderTraversal(self, root: TreeNode) -> List[int]:
stack = []
res = []
while root or stack:
while root:
stack.append(root)
root = root.left
if stack:
root = stack.pop()
res.append(root.val)
root = root.right
return res Binary Search Tree
def find(value, root):
node = root
while node is not None:
if node.data > value:
node = node.leftNode
elif node.data < value:
node = node.rightNode
else:
return True
return False
Stack & Queue
Increasing & Decreasing Stack
Sometimes you need to push index
def largestRectangleArea(self, height: List[int]) -> int:
area = 0
stack = []
height.append(0)
for i in range(len(height)):
while stack and height[i] < height[stack[-1]]:
h = height[stack.pop()]
width = i if not stack else i - stack[-1] - 1
area = max(area, h * width)
stack.append(i)
return areaHeap
When you do not care about the inside details
Comparator and Comparable
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
q = []
for index, each_list in enumerate(lists):
if each_list:
heapq.heappush(q, [each_list.val, index, each_list])
ans = []
dummy = ListNode(-1)
cur = dummy
while q:
v, i, node = heapq.heappop(q)
cur.next = node
cur = cur.next
if node.next:
heapq.heappush(q, [node.next.val, i, node.next])
return dummy.next
HashMap
Hash Function & Collision
HashSet? TreeSet?
HashMap + DFS/ArrayList?
idea of Hashing
def subarraySum(self, nums, k):
count = 0
sum_val = 0
hashmap = {0: 1}
for num in nums:
sum_val += num
if sum_val - k in hashmap:
count += hashmap[sum_val - k]
hashmap[sum_val] = hashmap.get(sum_val, 0) + 1
return count
Sort
Remember the similarity and difference
Merge Sort/ Quick Sort
In-place Sort
Collections.sort and Arrays.sort
Graph & Topology Sort
DAG
BFS/DFS
Course Schedule
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
degrees = [0 for _ in range(numCourses)] # how many courses need to finish if we want to take the course
pre = [[] for _ in range(numCourses)] # I am who's the prerequisites
for i, j in prerequisites: # O(E)
degrees[i] += 1
pre[j].append(i)
cur = []
for i in range(numCourses): # O(V)
if degrees[i] == 0: # the courses doesn't need to prerequisites
cur.append(i)
ans = []
while (cur): # O(V + E)
next = []
for i in cur:
for x in pre[i]:
degrees[x] -= 1
if degrees[x] == 0:
next.append(x)
ans += cur
cur = next
return len(ans) == numCourses
# TC, SC: O(V+E)Union Find
A different kind of tree
Help to count number of sets
class UnionFind:
def __init__(self, n):
self.list = [i for i in range(n)]
def union(self, a, b):
ancestor_a = self.find(a)
ancestor_b = self.find(b)
if ancestor_a == ancestor_b:
return False
else:
self.list[ancestor_b] = ancestor_a
return True
def find(self, k):
i = k
while i != self.list[i]:
i = self.list[i] # Here i is the root.
return iBit Manipulation
Small tricks
XOR
Single Number
OO & System Design
- Clarify the question
- Start with main objects
- Try link them up
- Action with examples
- Accuracy
- Performance
- Robustness
Python [Jo] Review 16
By ZhiTongGuiGu
Python [Jo] Review 16
- 72