Framework Engineering - Whiteboard Coding Patterns
Here is solutions for practice whiteboard coding in AXON.
1. User Key-Value Timestamp
from collections import defaultdict
class Location:
def __init__(self, latitude, longitude, timestamp):
self.latitude = latitude
self.longitude = longitude
self.timestamp = timestamp
def __str__(self):
return f"({self.latitude}, {self.longitude}, {self.timestamp})"
class LocationTracker:
def __init__(self):
self.locations = defaultdict(list)
def set(self, user_id: str, location: Location) -> None:
if user_id not in self.locations:
self.locations[user_id] = []
self.locations[user_id].append(location)
# Query time and binary search
def get(self, user_id: str, query_time: int) -> Location:
if user_id not in self.locations:
return Location(0, 0, 0)
userLocations = self.locations[user_id]
left, right = 0, len(userLocations) - 1
while left <= right:
mid = left + (right - left) // 2
if userLocations[mid].timestamp == query_time:
return userLocations[mid]
elif userLocations[mid].timestamp < query_time:
left = mid + 1
else:
right = mid - 1
return userLocations[left]
def main():
locationTracker = LocationTracker()
locationTracker.set(1, Location(2,5,1))
locationTracker.set(1, Location(3,6,3))
locationTracker.set(1, Location(7,4,6))
query_location = locationTracker.get(1, 2)
print(query_location)
main()
2. Encrypt, Descrypt, Hash Code
# Encrypt, Descrypt, Hash Code
class FuzzySearch:
ALPHABET_SIZE = 26
BASE_ORD = ord('a')
def __init__(self, secret: str) -> None:
self.secret = secret
self.block_size = len(secret)
def _idx2char(self, idx: int) -> chr:
# 1 -> a, 2 -> b, 3 -> c,...
char_idx = idx - 1 + self.BASE_ORD
return chr(char_idx)
def _char2idx(self, char: str) -> int:
# a -> 1, b -> 2, c -> 3,...
return ord(char) - self.BASE_ORD + 1
def _addChar(self, char1: str, char2: str) -> str:
v1 = self._char2idx(char1)
v2 = self._char2idx(char2)
return self._idx2char((v1 + v2) % self.ALPHABET_SIZE)
def _subChar(self, char1: str, char2: str) -> str:
v1 = self._char2idx(char1)
v2 = self._char2idx(char2)
return self._idx2char((v1 - v2 + self.ALPHABET_SIZE) % self.ALPHABET_SIZE)
def _addStr(self, str1: str, str2: str) -> str:
result = []
for c1, c2 in zip(str1, str2):
merged = self._addChar(c1, c2)
result.append(merged)
return ''.join(result)
def encrypt(self, data: str) -> str:
result = []
for i, char in enumerate(data):
secret_char = self.secret[i % self.block_size]
merged = self._addChar(char, secret_char)
result.append(merged)
return ''.join(result)
def decrypt(self, data: str) -> str:
result = []
for i, char in enumerate(data):
secret_char = self.secret[i % block_size]
merged = self._subChar(char, secret_char)
result.append(merged)
return ''.join(result)
def hash(self, data: str) -> str:
n = len(data)
acc = self.secret
for i in range(0, n - self.block_size, self.block_size):
block_str = self.data[i, i + self.block_size]
acc = self._addStr(acc, block_str)
return acc
fuzzySearch = FuzzySearch('abc')
print(fuzzySearch.encrypt('abcdef'))
3. Insert/Delete/Random O(1)
# Insert/Delete/Random O(1)
from collections import defaultdict
import random
class InsertDeleteRandom:
def __init__(self):
self.arr = []
self.pos = defaultdict(set)
def insert(self, num):
if num not in self.pos:
self.pos[num] = set()
# Add to array
self.arr.append(num)
# Add the index
self.pos[num].add(len(self.arr) - 1)
def remove(self, num):
if num not in self.pos:
return
# Remove idx
remove_idx = self.pos[num].pop()
last_val = self.arr[-1]
# Replace the remove_idx by last_val and pop last_val
self.arr[remove_idx] = last_val
# Pop last_val and remove index
# self.pos[num].discard(remove_idx)
self.pos[last_val].add(remove_idx)
# Remove last val
self.pos[last_val].discard(len(self.arr) - 1)
self.arr.pop()
def random(self):
return random.choice(self.arr)
def main():
solution = InsertDeleteRandom()
solution.insert(1)
solution.insert(3)
solution.insert(3)
solution.insert(5)
solution.remove(3)
print(solution.random())
main()
4. Police Parking Garage
from collections import deque
from typing import List
class PoliceParkingGarage:
def __init__(self, carIds: List[str]):
self.stack = deque(carIds)
self.queue = deque()
def check_out(self):
if not self.stack:
raise Exception("No car for check out")
# Pop the car
top = self.stack.pop()
# Add to queue
self.queue.append(top)
return top
def check_in(self):
if not self.queue:
raise Exception("Not car to check in")
# Pop the left car
leftCar = self.queue.popleft()
# Add to stack
self.stack.append(leftCar)
return leftCar
def getNextCars(self, n: int) -> List[int]:
tempStack = self.stack
tempQueue = self.queue
# All the cars check-in
while tempQueue:
leftCar = tempQueue.popleft()
tempStack.append(leftCar)
result = []
while tempStack:
top = tempStack.pop()
result.append(top)
return result[:n]
def main():
garage = PoliceParkingGarage([1,2,3,4,5])
print(garage.check_out())
print(garage.check_in())
print(garage.getNextCars(3))
main()
5. Inorder Shuffle
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
m, n = len(s1), len(s2)
k = len(s3)
if m + n != k:
return False
def dfs(i, j):
if i == m and j == n:
return True
if i < m and s1[i] == s3[i + j]:
if dfs(i + 1, j):
return True
if j < n and s2[j] == s3[i + j]:
if dfs(i, j + 1):
return True
return False
return dfs(0, 0)
def main():
solution = Solution()
s1 = "aaaa"
s2 = "bbbb"
s3 = "aabbbbaa"
print(solution.isInterleave(s1, s2, s3))
main()
# Top-down solution
def isInterLeave(self, s1: str, s2: str, s3: str) -> bool:
n = len(s1)
m = len(s2)
k = len(s3)
if k != n + m:
return False
dp = {}
def dfs(i, j):
# Step 1: Base case
if i == n and j == m:
return True
k = i + j
if (i, j) in dp:
return dp[(i, j)]
# Step 2: Backtrack
res = False
if i < len(s1) and s1[i] == s3[k]:
res = dfs(i + 1, j)
if j < len(s2) and s2[j] == s3[k]:
res = dfs(i, j + 1)
dp[(i, j)] = res
return res
return dfs(0, 0)
# Bottom-up solution
def isInterLeave(self, s1: str, s2: str, s3: str) -> bool:
m, n, k = len(s1), len(s2), len(s3)
if m + n != k:
return False
# Step 1: Base case
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
for j in range(1, n + 1):
dp[0][j] = dp[0][j - 1] and s2[j - 1] = s3[j - 1]
for i in range(1, m + 1):
dp[i][0] = dp[i - 1][0] and s1[i - 1] = s3[i - 1]
# Step 2: Update
for i in range(m + 1):
for j in range(n + 1):
dp[i][j] = (dp[i - 1][j] and s1[i - 1] == s3[i + j - 1])
or (dp[i][j - 1] and s2[j - 1] == s3[i + j - 1])
return dp[m][n]
6. K Closest Points to Origin
import heapq
class Solution:
def _distance(self, x, y):
return x ** 2 + y ** 2
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
maxHeap = []
for x, y in points:
# heap push
dist = -self._distance(x, y)
heapq.heappush(maxHeap, [dist, x, y])
if len(maxHeap) > k:
heapq.heappop(maxHeap)
res = []
while maxHeap:
dist, x, y = heapq.heappop(maxHeap)
res.append([x, y])
return res
7. Number of Islands
class Solution:
# DFS
def numIslands(self, grid: List[List[str]]) -> int:
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
ROWS, COLS = len(grid), len(grid[0])
def dfs(r, c):
# Base case
if r < 0 or r >= ROWS or c < 0 or c >= COLS or grid[r][c] == "0":
return
# Backtrack or add visited
grid[r][c] = "0"
for dr, dc in directions:
dfs(r + dr, c + dc)
count = 0
for r in range(ROWS):
for c in range(COLS):
if grid[r][c] == "1":
dfs(r, c)
count += 1
return count
from collections import deque
class Solution:
# DFS
def numIslands(self, grid: List[List[str]]) -> int:
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
ROWS, COLS = len(grid), len(grid[0])
def bfs(r, c):
q = deque()
grid[r][c] = "0"
q.append([r, c])
while q:
row, col = q.popleft()
for dr, dc in directions:
nr, nc = row + dr, col + dc
# Base case
if nr < 0 or nr >= ROWS or nc < 0 or nc >= COLS or grid[nr][nc] == "0":
continue
# Backtrack
q.append([nr, nc])
grid[nr][nc] = "0"
count = 0
for r in range(ROWS):
for c in range(COLS):
if grid[r][c] == "1":
bfs(r, c)
count += 1
return count
8. Valid Palindrome
class Solution:
def alphaNum(self, c):
return (ord('A') <= ord(c) <= ord('Z') or
ord('a') <= ord(c) <= ord('z') or
ord('0') <= ord(c) <= ord('9'))
def isPalindrome(self, s: str) -> bool:
left, right = 0, len(s) - 1
while left <= right:
while left < right and not self.alphaNum(s[left]):
left += 1
while left < right and not self.alphaNum(s[right]):
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True
9. N-Queens
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
col = set()
posDiag = set()
negDiag = set()
res = []
board = [["."] * n for i in range(n)]
def backtrack(r):
# Base case
if r == n:
copy = ["".join(row) for row in board]
res.append(copy)
return
for c in range(n):
if c in col or (r + c) in posDiag or (r - c) in negDiag:
continue
# Current
col.add(c)
posDiag.add(r + c)
negDiag.add(r - c)
board[r][c] = "Q"
# Backtrack another row
backtrack(r + 1)
col.remove(c)
posDiag.remove(r + c)
negDiag.remove(r - c)
board[r][c] = "."
backtrack(0)
return res
10. Roman to Integer
class Solution:
def romanToInt(self, s: str) -> int:
roman = {
"I": 1,
"V": 5,
"X": 10,
"L": 50,
"C": 100,
"D": 500,
"M": 1000
}
n = len(s)
res = 0
for i in range(n):
if i + 1 < len(s) and roman[s[i]] < roman[s[i + 1]]:
res -= roman[s[i]]
else:
res += roman[s[i]]
return res
class Solution:
def intToRoman(self, num: int) -> str:
roman = {
"M": 1000,
"CM": 900,
"D": 500,
"CD": 400,
"C": 100,
"XC": 90,
"L": 50,
"XL": 40,
"X": 10,
"IX": 9,
"V": 5,
"IV": 4,
"I": 1
}
res = ""
for key, value in roman.items():
count = num // value
if count:
res += count * key
num -= count * value
return res
11. Edit Distance
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
def dfs(i, j):
# Base case
if i == m:
return n - j
if j == n:
return m - i
if word1[i] == word2[j]:
return dfs(i + 1, j + 1)
else:
# Backtrack
res = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1))
return res + 1
return dfs(0, 0)
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = {}
def dfs(i, j):
# Base case
if i == m:
return n - j
if j == n:
return m - i
if (i, j) in dp:
return dp[(i, j)]
if word1[i] == word2[j]:
dp[(i, j)] = dfs(i + 1, j + 1)
else:
# Backtrack
res = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1))
dp[(i, j)] = res + 1
return dp[(i, j)]
return dfs(0, 0)
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
# Base case
n, m = len(word1), len(word2)
dp = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(n + 1):
# Convert word[:i] to empty string
dp[i][0] = i
for j in range(m + 1):
# Convert empty string to word[:j]
dp[0][j] = j
# Update
for i in range(1, n + 1):
for j in range(1, m + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1])
return dp[n][m]
12. Longest Substring Without Repeating Characters
- Keep l = 0, increase right => If reach the condition, shrink the left, left += 1.
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
charSet = set()
l = 0
res = 0
for r in range(len(s)):
while s[r] in charSet:
charSet.remove(s[l])
l += 1
charSet.add(s[r])
res = max(res, r - l + 1)
return res
13. Design Twitter
from collections import defaultdict
from collections import deque
import heapq
class Twitter:
MAX_SIZE = 10
def __init__(self):
# Hashmap userId and [tweetID]
self.tweets = defaultdict(deque)
# Remove in hashset o(1)
self.following = defaultdict(set)
# Add current timestamp
self.curr_time = 0
# O(1)
def postTweet(self, userId: int, tweetId: int) -> None:
if userId not in self.tweets:
self.tweets[userId] = deque()
self.tweets[userId].append([self.curr_time, tweetId])
if len(self.tweets[userId]) > self.MAX_SIZE:
self.tweets[userId].popleft()
self.curr_time += 1
# Naive solution, optimize later
def getNewsFeed(self, userId: int) -> List[int]:
# Skip elements in small time
minHeap = []
# Total people he is following
userList = list(self.following[userId]) + [userId]
for user in userList:
for post in self.tweets[user]:
heapq.heappush(minHeap, post)
if len(minHeap) > self.MAX_SIZE:
heapq.heappop(minHeap)
# Get only top 10 recent heap
result = []
while minHeap:
time, tweetID = heapq.heappop(minHeap)
result.append(tweetID)
return result[::-1]
def follow(self, followerId: int, followeeId: int) -> None:
if followerId == followeeId:
return
if followerId not in self.following:
self.following[followerId] = set()
self.following[followerId].add(followeeId)
def unfollow(self, followerId: int, followeeId: int) -> None:
if followerId == followeeId:
return
if followeeId not in self.following[followerId]:
return
self.following[followerId].discard(followeeId)
2. Template
2.1. DFS Tree
# DFS Tree
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def dfs(node, path, res):
# Base case
if node is None:
return
# Current
path.append(node.val)
# Backtrack
if not node.left and not node.right:
res.append(path[:])
else:
dfs(node.left, path, res)
dfs(node.right, path, res)
# Backtrack
path.pop()
def main():
# Build example tree
# 1
# / \
# 2 3
# /
# 4
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
res = []
dfs(root, [], res)
print("All root-to-leaf paths:")
print(res)
if __name__ == "__main__":
main()
2.2. BFS Tree
from collections import deque
class TreeNode:
def __init__(self, val = 0, left = None, right = None):
self.val = val
self.left = left
self.right = right
# Level traversal
def bfs(root):
queue = deque()
queue.append(root)
result = []
while queue:
level = len(queue)
curr = []
for i in range(level):
# Current
node = queue.popleft()
curr.append(node.val)
# Backtrack
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(curr)
return result
def main():
# Build example tree
# 1
# / \
# 2 3
# /
# 4
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
res = bfs(root)
print("BFS Res:")
print(res)
if __name__ == "__main__":
main()
2.3. BFS Graph
from collections import deque
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B'],
'F': ['C']
}
def bfs(root):
queue = deque()
# Node
queue.append(root)
# Visited
visited = set()
while queue:
node = queue.popleft()
print(node + " ")
visited.add(node)
# Neighbor
for neighbor in graph[node]:
if neighbor not in visited:
queue.append(neighbor)
bfs('B')
2.4. DFS Graph
from collections import deque
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B'],
'F': ['C']
}
visited = set()
def dfs(node):
# Base case
if not node:
return
# Node
print(node)
# Visited
visited.add(node)
# Neighbor
for neighbor in graph[node]:
if neighbor not in visited:
dfs(neighbor)
dfs('B')
2.5. BFS Matrix
from collections import deque
grid = [
[1, 1, 0, 1, 1],
[0, 1, 0, 1, 0],
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1]
]
ROWS, COLS = len(grid), len(grid[0])
def bfs(r, c):
# queue
queue = deque()
# visited
visited = set()
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
queue.append((r, c))
# current
while queue:
row, col = queue.popleft()
visited.add((r, c))
print(f"({row}, {col})")
# neighbor
for dr, dc in directions:
nr, nc = r + dr, c + dc
if nr < 0 or nr >= ROWS or nc < 0 or nc >= COLS:
continue
if (nr, nc) not in visited and grid[nr][nc] == 1:
queue.append((nr, nc))
visited.add((nr, nc))
for i in range(ROWS):
for j in range(COLS):
if grid[i][j] == 1:
bfs(i, j)
2.6. DFS Matrix
from collections import deque
grid = [
[1, 1, 0, 1, 1],
[0, 1, 0, 1, 0],
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1]
]
ROWS, COLS = len(grid), len(grid[0])
count = 0
visited = set()
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
def dfs(r, c):
# Base case
if r < 0 or r >= ROWS or c < 0 or c >= COLS or grid[r][c] == 0 or (r, c) in visited:
return
# Node
# Visited
visited.add((r, c))
# Neighbor
for dr, dc in directions:
nr, nc = r + dr, c + dc
dfs(nr, nc)
for i in range(ROWS):
for j in range(COLS):
if grid[i][j] == 1 and (i, j) not in visited:
dfs(i, j)
count += 1
print(count)
2.7. Binary Search
arr = [1, 2, 3, 4, 5]
def binary_search(arr, target):
n = len(arr)
left, right = 0, n - 1
while left < right:
mid = left + (right - left) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
print(binary_search(arr, 7))
2.7. Binary Search Left Most
arr = [1, 2, 3, 3, 4, 4, 4, 5]
def binary_search(arr, target):
n = len(arr)
left, right = 0, n - 1
while left <= right:
mid = left + (right - left) // 2
if arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
print(binary_search(arr, 4))
2.8. Binary Search Right Most
arr = [1, 2, 3, 3, 4, 4, 4, 5]
def binary_search(arr, target):
n = len(arr)
left, right = 0, n - 1
while left <= right:
mid = left + (right - left) // 2
if arr[mid] <= target:
left = mid + 1
else:
right = mid - 1
return left - 1
print(binary_search(arr, 4))
2.9. Remove Duplicates
arr = [1,2,3,3,4,5]
def remove_duplicates(arr):
left = 0
n = len(arr)
for right in range(n):
if right > 0 and arr[right] == arr[right - 1]:
continue
arr[left] = arr[right]
left += 1
return arr[:left]
arr = remove_duplicates(arr)
print(arr)
2.10. Two Sum
arr = [1,2,3,3,4,5]
target = 5
def two_sum(arr, target):
n = len(arr)
left, right = 0, n - 1
res = []
while left <= right:
if arr[left] + arr[right] == target:
res.append([arr[left], arr[right]])
left += 1
right -= 1
elif arr[left] + arr[right] < target:
left += 1
else:
right -= 1
return res
print(two_sum(arr, target))
2.11. Sliding Window (Fixed Size)
nums = [1,3,2,1,2,5]
k = 3
def max_sum_size_k(nums, k):
left = 0
n = len(nums)
curr = 0
maxSum = 0
for right in range(n):
curr += nums[right]
if right - left + 1 == k:
maxSum = max(maxSum, curr)
curr -= nums[left]
left += 1
return maxSum
print(max_sum_size_k(nums, k))
2.12. Longest Substring Without Repeating Characters
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
left = 0
n = len(s)
charSet = set()
maxLength = 0
for right in range(n):
while s[right] in charSet:
charSet.discard(s[left])
left += 1
charSet.add(s[right])
maxLength = max(maxLength, right - left + 1)
return maxLength
2.13. Top K - Max Heap
import heapq
arr = [2,1,4,3,6,5,8,7]
k = 3
def maxHeapTopK(arr, k):
# Get 3 smallest value
maxHeap = []
for i, num in enumerate(arr):
heapq.heappush(maxHeap, [-num, i])
if len(maxHeap) > k:
heapq.heappop(maxHeap)
result = []
while maxHeap:
val, i = heapq.heappop(maxHeap)
result.append(-val)
return result[::-1]
def minHeapTopK(arr, k):
# Get 3 largest value
return
print(maxHeapTopK(arr, k))
2.14. Top K - Min Heap
import heapq
arr = [2,1,4,3,6,5,8,7]
k = 3
def maxHeapTopK(arr, k):
# Get 3 smallest value
maxHeap = []
for i, num in enumerate(arr):
heapq.heappush(maxHeap, [-num, i])
if len(maxHeap) > k:
heapq.heappop(maxHeap)
result = []
while maxHeap:
val, i = heapq.heappop(maxHeap)
result.append(-val)
return result[::-1]
def minHeapTopK(arr, k):
# Get 3 largest value
minHeap = []
for i, num in enumerate(arr):
heapq.heappush(minHeap, [num, i])
if len(minHeap) > k:
heapq.heappop(minHeap)
result = []
while minHeap:
val, i = heapq.heappop(minHeap)
result.append(val)
return result[::-1]
print(minHeapTopK(arr, k))
2.15. Monotonic Stack
Note: While stack, pop it.
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
stack = []
n = len(temperatures)
result = [0] * n
for i in range(n):
while stack and temperatures[i] > temperatures[stack[-1]]:
result[stack[-1]] = i - stack[-1]
stack.pop()
stack.append(i)
return result
3. System Design - Physical Envidences
3.1. Context
Precinct
└── LockerSet
└── Locker
└── Box
└── Evidence
Case
└── Box (1-to-many)
User
└── Role / Permission
3.2. High Level Design

3.3. API Design
POST /cases
POST /cases/{caseId}/boxes
POST /evidence/checkin
POST /evidence/checkout
POST /permissions/grant
POST /permissions/revoke
GET /cases/{caseId}/audit
3.4. Data Model
- users:
user_id (PK)
role ENUM(DETECTIVE, CHIEF, ADMIN)
precinct_id
- lockers:
locker_id (PK)
locker_set_id
size ENUM(S,M,L)
status ENUM(AVAILABLE, OCCUPIED, MAINTENANCE)
- boxes:
box_id (PK)
case_id (FK)
locker_id (FK)
status ENUM(IN, OUT)
- evidence (USB, documents)
evidence_id (PK)
box_id (FK)
description
December 16, 2025