1. Sliding Window
Use Case: When dealing with contiguous subarrays or substrings.
# Example: Maximum Sum Subarray of Size K
def max_subarray_sum(arr, k):
max_sum, window_sum = 0, sum(arr[:k])
for i in range(len(arr) - k):
window_sum += arr[i + k] - arr[i]
max_sum = max(max_sum, window_sum)
return max_sum
2. Two Pointers
Use Case: When dealing with sorted arrays or linked lists.
# Example: Two Sum (Sorted Input)
def two_sum(numbers, target):
left, right = 0, len(numbers) - 1
while left < right:
current_sum = numbers[left] + numbers[right]
if current_sum == target:
return [left + 1, right + 1]
elif current_sum < target:
left += 1
else:
right -= 1
return []
3. Fast & Slow Pointers (Floyd’s Cycle Detection)
Use Case: Detecting cycles in linked lists or arrays.
# Example: Detect Cycle in Linked List
def has_cycle(head):
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
4. Merge Intervals
Use Case: When working with overlapping intervals.
# Example: Merge Intervals
def merge(intervals):
intervals.sort(key=lambda x: x[0])
merged = []
for interval in intervals:
if merged and merged[-1][1] >= interval[0]:
merged[-1][1] = max(merged[-1][1], interval[1])
else:
merged.append(interval)
return merged
5. Cyclic Sort
Use Case: Finding missing numbers or sorting numbers in a given range.
# Example: Find Missing Number
def find_missing_number(nums):
i, n = 0, len(nums)
while i < n:
j = nums[i]
if j < n and nums[i] != nums[j]:
nums[i], nums[j] = nums[j], nums[i]
else:
i += 1
for i in range(n):
if nums[i] != i:
return i
return n
6. Backtracking
Use Case: Solving problems that require exploring all possibilities.
# Example: Generate Parentheses
def generate_parentheses(n):
result = []
def backtrack(s='', left=0, right=0):
if len(s) == 2 * n:
result.append(s)
return
if left < n:
backtrack(s + '(', left + 1, right)
if right < left:
backtrack(s + ')', left, right + 1)
backtrack()
return result
7. Dynamic Programming (Bottom-Up)
Use Case: Solving problems that have overlapping subproblems.
# Example: Fibonacci Sequence (DP Approach)
def fibonacci(n):
if n <= 1:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
8. BFS/DFS
Use Case: Traversing trees, graphs, and shortest path problems.
# Example: BFS Traversal
from collections import deque
def bfs(graph, start):
visited = set()
queue = deque([start])
while queue:
node = queue.popleft()
if node not in visited:
visited.add(node)
print(node, end=' ')
queue.extend(graph[node] - visited)
9. Bit Manipulation
Use Case: Solving problems efficiently using binary operations.
# Example: Single Number (XOR approach)
def single_number(nums):
result = 0
for num in nums:
result ^= num
return result
10. Heap (Priority Queue)
Use Case: Finding K largest/smallest elements efficiently.
# Example: Kth Largest Element
import heapq
def find_kth_largest(nums, k):
return heapq.nlargest(k, nums)[-1]
11. Union-Find (Disjoint Set Union)
Use Case: Problems involving connected components in graphs.
# Example: Number of Connected Components in Undirected Graph
def count_components(n, edges):
parent = list(range(n))
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(x, y):
root_x = find(x)
root_y = find(y)
if root_x != root_y:
parent[root_x] = root_y
for x, y in edges:
union(x, y)
return len(set(find(x) for x in range(n)))
12. Topological Sorting
Use Case: Directed Acyclic Graph (DAG) problems.
# Example: Course Schedule (Detect Cycle in Graph)
from collections import defaultdict, deque
def can_finish(num_courses, prerequisites):
graph = defaultdict(list)
indegree = [0] * num_courses
for dest, src in prerequisites:
graph[src].append(dest)
indegree[dest] += 1
queue = deque([i for i in range(num_courses) if indegree[i] == 0])
visited = 0
while queue:
course = queue.popleft()
visited += 1
for neighbor in graph[course]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)
return visited == num_courses
13. Trie (Prefix Tree)
Use Case: Efficient string search and prefix matching.
# Example: Implement Trie (Prefix Tree)
class TrieNode:
def __init__(self):
self.children = {}
self.is_end_of_word = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end_of_word = True
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end_of_word
def starts_with(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True
14. Segment Tree / Fenwick Tree
Use Case: Range queries and updates efficiently.
# Example: Range Sum Query (Fenwick Tree)
class FenwickTree:
def __init__(self, size):
self.tree = [0] * (size + 1)
def update(self, index, delta):
while index < len(self.tree):
self.tree[index] += delta
index += index & -index
def query(self, index):
sum = 0
while index > 0:
sum += self.tree[index]
index -= index & -index
return sum
15. Binary Search on Answer
Use Case: Problems that require minimizing or maximizing some value under constraints.
# Example: Koko Eating Bananas
def min_eating_speed(piles, h):
def can_eat_all(speed):
return sum((pile + speed - 1) // speed for pile in piles) <= h
left, right = 1, max(piles)
while left < right:
mid = (left + right) // 2
if can_eat_all(mid):
right = mid
else:
left = mid + 1
return left