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