Two Pointers - Opposite Ends (Converging)
Python Implementation
ef two_pointers(arr1):
left = 0
right = len(arr1) -1
while left < right:
# Do some logic depending on the problem
# Do some logic to determine the following
# - left += 1
# - right -= 1
# - both left += 1 and right -=1
passTwo Pointers - Multiple Arrays/Strings (Parallel)
Python Implementation
def two_pointers(arr1, arr2):
i = j = 0
while i < len(arr1) and j < len(arr2):
# Do some logic depending on the problem
# Do some logic to decide on the following:
# 1. i += 1
# 2. j += 1
# 3. i += 1 and j += 1
pass
# Make sure both iterables are exhausted
# Only one of these loops will run
while i < len(arr1):
# Do some logic depending on the problem
i += 1
pass
while j < len(arr2):
# Do some logic depending on the problem
j += 1
pass
Sliding Window – Dynamic Window
Python Implementation
def sliding_window(arr):
left = 0
for right in range(len(arr)):
# Do some logic to add arr[right] to window
while WINDOW_IS_INVALID:
# Do some logic to remove arr[left] from the window
left += 1
pass
# Do some logic to update the answerSliding Window - Fixed Window
Python Implementation
def sliding_window(arr, k):
curr = 0 # Some data to track the window
# Build the first window
for i in range(k):
# Do something with curr or other variables
pass
ans = curr
# Slide the window
for i in range(k, len(arr)):
# Add arr[i] to window
# Remove arr[i-k] from window
# Update ans
pass
return ansPrefix Sum
Python Implementation
```python
def prefix_sum(nums: list[int]):
prefix = [nums[0]]
for i in range(1, len(nums)):
prefix.append(nums[i] + prefix[-1])
return prefixSum between i and j is:
# sum = prefix[j] - prefix[i] + nums[i]
~~~
Describe an algorithm to efficiently build a string
arr is a list of characters
def build_string(s: str):
arr = []
# Convert a string into an array
for c in s:
arr.append(c)
# Convert an array to a string
return "".join(arr)
Describe an algorithm to find the number of subarrays that fit an exact criteria
from collections import defaultdict
def fn(arr, k):
counts = defaultdict(int)
counts[0] = 1
ans = curr = 0
for num in arr:
# do logic to change curr
ans += counts[curr - k]
counts[curr] += 1
return ansDescribe an algorithm to implement fast and slow pointers in a linked list
def fn(head):
slow = head
fast = head
ans = 0
while fast and fast.next:
# do logic
slow = slow.next
fast = fast.next.next
return ansDescribe an algorithm to return the middle of a linked list
def fn(head):
slow = head
fast = head
while slow and fast.next:
slow = slow.next
fast = fast.next.next
return slow.valDescribe an algorithm to find cycles in a linked list
def fn(head):
slow = head
fast = head
while slow and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return FalseDescribe an algorithm to reverse a linked list
def fn(head):
curr = head
prev = None
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prevDescribe an algorithm to build/maintain a monotonic increasing stack
def fn(arr):
stack = []
ans = 0
for num in arr:
# for monotonic decreasing, just flip the > to <
while stack and stack[-1] > num:
# do logic
stack.pop()
stack.append(num)
return ansDescribe and algorithm to perform a DFS (recursive) on a binary tree
def dfs(root):
if not root:
return
ans = 0
# do logic
dfs(root.left)
dfs(root.right)
return ansDescribe an algorithm to perform a DFS (iterative) on a binary tree
def dfs(root):
stack = [root]
ans = 0
while stack:
node = stack.pop()
# do logic
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return ansDescribe and algorithm to perform preorder Depth First Search (DFS) on a binary tree
def preorder_dfs(node):
if node == None:
return
print(node.value)
preorder_dfs(node.left)
preorder_dfs(node.right)
returnDescribe and algorithm to perform inorder Depth First Search (DFS) on a binary tree
def inorder_dfs(node):
if node == None:
return
inorder_dfs(node.left)
print(node.value)
inorder_dfs(node.right)
returnDescribe and algorithm to perform postorder Depth First Search (DFS) on a binary tree
def postorder_dfs(node):
if node == None:
return
postorder_dfs(node.left)
postorder_dfs(node.right)
print(node.value)
returnDescribe an algorithm to perform Breadth First Search (BFS) on a binary tree
from collections import deque
def bfs(root):
queue = deque([root])
while queue:
nodes_in_current_level = len(queue)
# Do some logic on the current level
for _ in range(nodes_in_current_level):
node = queue.popleft()
# Do some logic on the current node
print(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)Describe an algorithm to insert a value into a Binary Search Tree (BST)
def insertIntoBST(root, val):
if not root:
return TreeNode(val)
if val < root.val:
root.left = insertInotBST(root.left, val)
else:
root.right = insertInotBST(root.right, val)
return rootDescribe an algorithm to build an adjacency list given an array of directed edges
from collections import defaultdict
def build_graph(edges):
graph = defaultdict(list)
for x, y in edges:
graph[x].append(y)
# uncomment if undirected
#graph[y].append(x)
return graphDescribe an algorithm to build a graph given an adjacency matrix
from collections import defaultdict
def build_graph_from_adjacency_matrix(adj_matrix):
graph = defaultdict(list)
n = len(adj_matrix)
m = len(adj_matrix[0])
for i in range(n):
for j in range(m):
if adj_matrix[i][j] == 1:
graph[i].append(j)
return graphDescribe an algorithm to perform a Depth First Search (DFS) on a graph
# build graph
seen = set()
def dfs(node):
for neighbor in graph[node]:
if neighbor not in seen:
print(neighbor)
seen.add(neighbor)
dfs(neighbor)
for i in range(len(graph)):
if i not in seen:
print(i)
seen.add(i)
dfs(i)Describe and algorithm to perform a Depth First Search (DFS) to find the number of islands given a a binary matrix
def island_count(grid):
m = len(grid)
n = len(grid[0])
directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
seen = set()
islands = 0
def is_valid(row: int, col: int) -> bool:
return 0 <= row < m and 0 <= col < n and grid[row][col] == 1:
def dfs(row, col):
size = 1
for dx, dy in directions:
next_row, next_col = row + dy, col + dx
if is_valid(next_row, next_col) and (next_row, next_col) not in seen:
seen.add((next_row, next_col))
size += dfs(next_row, next_col)
return size
for row in range(m):
for col in range(n):
if (row, col) not in seen and is_valid(row,col):
islands += 1
seen.add((row,col))
dfs(row,col)
return islandsDescribe an algorithm to perform a Breadth First Search (BFS) to find the shortest path given a a binary matrix
from collections import deque
def shortest_path_binary_matrix(grid):
if grid[0][0] == 1:
return -1
n = len(grid)
m = len(grid[0])
seen = set((0,0))
queue = deque([(0,0,1)]) # right, left, steps
directions = [
(1,0),
(1,1),
(0,1),
(-1,1),
(-1,0),
(-1,-1),
(0,-1),
(1,-1)
]
def is_valid(row, col):
return 0 <= row < m and 0 <= col < n and grid[row][col] == 0
while queue:
row, col, steps = queue.popleft()
if (row, col) == (n-1, m-1):
return steps
for dx, dy in directions:
next_row, next_col = row + dy, col + dx
if is_valid(next_row, next_col) and (next_row, next_col) not in seen:
seen.add((next_row, next_col))
queue.append((next_row, next_col, steps + 1))
return -1