LeetCode Patterns Cheat Sheet, with Code Templates

The 18 patterns behind most LeetCode and coding interview problems, one block each: the words in a problem that point to the pattern, a code template to start from, its usual cost, and three problems to practise it on.

A template is a starting shape, not an answer to paste. Read the signal first, then adapt the template to the problem. The pattern guides explain each one in full, and the DSA interview questions page lists every problem with its answer.

01. Two Pointers

Full guide

When to use it

The array is sorted, or sorting it does not lose information, and the question asks for a pair or triplet meeting some condition. "Find two numbers that sum to X", "the container holding the most water", "remove duplicates in place" are all this pattern. If your first instinct is a nested loop over the same array, check whether two pointers collapses it.

Template (Python)

def two_sum(nums, target):
    left = 0
    right = len(nums) - 1

    while left < right:
        current_sum = nums[left] + nums[right]

        if current_sum == target:
            return True
        elif current_sum < target:
            left += 1
        else:
            right -= 1
            
    return False

From Introduction to Two Pointers, with a step-by-step visualizer.

Complexity

O(n) time and O(1) extra space, against O(n²) for the nested-loop version. When sorting is required first, the sort dominates at O(n log n).

02. Hash Map

Full guide

When to use it

The brute force is a nested loop whose inner loop asks "have I seen X before?", "how many times?" or "which items share this?" — a hash map answers each.

Template (Python)

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        seen = {}
        for i, num in enumerate(nums):
            need = target - num
            if need in seen:
                return [seen[need], i]
            seen[num] = i
        return []

From Two Sum, with a step-by-step visualizer.

Complexity

O(n) time on average for one pass with O(1) average lookups, paid for with O(n) extra space for the map — against O(n²) time and O(1) space for the nested loop.

03. Sliding Window

Full guide

When to use it

The problem asks for the longest, shortest, or best contiguous subarray or substring satisfying a condition. The word "contiguous" is the tell, as is a fixed window size k. "Longest substring without repeating characters" and "maximum sum subarray of size k" are the canonical forms.

Template (Python)

def lengthOfLongestSubstring(s: str) -> int:
    seen = {}
    left = 0
    max_len = 0

    for right, char in enumerate(s):
        # Specific Logic: If char seen within current window
        if char in seen and seen[char] >= left:
            left = seen[char] + 1
        
        seen[char] = right
        max_len = max(max_len, right - left + 1)
        
    return max_len

From Introduction to Sliding Window, with a step-by-step visualizer.

Complexity

O(n) time — each index enters and leaves the window at most once — against O(n²) or O(n³) for recomputing every subarray.

04. Intervals

Full guide

When to use it

The input is a list of start/end pairs — meetings, bookings, ranges — and the question involves merging, overlap, insertion or counting concurrency. If the input looks like [[1,3],[2,6],[8,10]], this is the pattern.

Template (Python)

def solveIntervals(intervals):
    if not intervals: return []
    # 1. Sort by start time
    intervals.sort(key=lambda x: x[0])
    
    result = []
    
    # 2. Iterate and process
    for interval in intervals:
        if not result or interval[0] > result[-1][1]:
            # No overlap, add new
            result.append(interval)
        else:
            # Overlap, merge or process logic
            result[-1][1] = max(result[-1][1], interval[1])
            
    return result

From Intervals Overview, with a step-by-step visualizer.

Complexity

O(n log n), dominated by the sort; the sweep itself is O(n). Choosing the wrong sort key is the most common failure, and it is usually end time rather than start time.

05. Linked List

Full guide

When to use it

The input is a list rather than an array, and the question asks about cycles, the nth node from the end, reordering, or comparing the list against itself. The giveaway constraint is "do it in one pass" or "use O(1) extra space" — both rule out copying into an array.

Template (Python)

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# Traversal
current = head
while current:
    print(current.val)
    current = current.next

From Linked List Overview, with a step-by-step visualizer.

Complexity

O(n) time and O(1) extra space for the pointer techniques. Copying into an array also gives O(n) time but costs O(n) space, which is usually the thing being tested.

06. Stack

Full guide

When to use it

The problem asks for the next greater or smaller element, needs matching pairs validated (brackets, tags), or involves undoing and revisiting recent state. Histogram and temperature problems are monotonic stack in disguise.

Template (Python)

stack = []
stack.append(x)       # push
top = stack[-1]       # peek
stack.pop()           # pop
is_empty = not stack

From Stack Overview, with a step-by-step visualizer.

Complexity

O(n) time, because every element is pushed once and popped at most once, against O(n²) for scanning forward from each position.

07. Binary Search

Full guide

When to use it

The data is sorted or rotated-sorted, or — more often — the answer lies in a numeric range and there is a monotonic feasibility check ("if capacity C works, C+1 also works"). "Minimum capacity to ship packages in D days" is binary search on the answer.

Template (Python)

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left, right = 0, len(nums) - 1
        
        while left <= right:
            mid = left + (right - left) // 2
            
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
                
        return -1

From Binary Search, with a step-by-step visualizer.

Complexity

O(log n) per search. Binary searching an answer range costs O(log(range) × cost of the feasibility check).

08. Heap / Priority Queue

Full guide

When to use it

The question says "top k", "k largest", "k closest", "median of a stream", or requires repeatedly taking the current minimum — which is what makes it the engine inside Dijkstra and k-way merges.

Template (Python)

heap = []

def push(val):
    heap.append(val)
    i = len(heap) - 1
    while i > 0 and heap[(i - 1) // 2] > heap[i]:
        parent = (i - 1) // 2
        heap[i], heap[parent] = heap[parent], heap[i]
        i = parent

def peek():
    return heap[0]

def pop():
    top = heap[0]
    last = heap.pop()
    if heap:
        heap[0] = last
        i = 0
        while True:
            left, right, smallest = 2 * i + 1, 2 * i + 2, i
            if left < len(heap) and heap[left] < heap[smallest]:
                smallest = left
            if right < len(heap) and heap[right] < heap[smallest]:
                smallest = right
            if smallest == i:
                break
            heap[i], heap[smallest] = heap[smallest], heap[i]
            i = smallest
    return top

From Heap Overview, with a step-by-step visualizer.

Complexity

O(n log k) for a top-k pass against O(n log n) for a full sort. Peeking is O(1); push and pop are O(log n).

09. Depth-First Search

Full guide

When to use it

You need to explore every node, test whether a path exists, work with tree structure, or compute something that depends on a node’s children before the node itself — the postorder shape behind most tree DP.

Template (Python)

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        result = []

        def dfs(node):
            if not node:
                return
            result.append(node.val)
            dfs(node.left)
            dfs(node.right)

        dfs(root)
        return result

From Introduction to DFS, with a step-by-step visualizer.

Complexity

O(V + E) time. Space is O(h) for the recursion stack, where h is depth — which is O(n) in the worst case for a skewed tree.

10. Breadth-First Search

Full guide

When to use it

The question asks for the shortest path or minimum number of steps in an unweighted graph or grid, or for something computed per level ("level order traversal", "rightmost node of each row"). "Minimum number of moves" almost always means BFS.

Template (Python)

from collections import deque


class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        result = []
        if not root:
            return result
        queue = deque([root])
        while queue:
            size = len(queue)
            level = []
            for _ in range(size):
                node = queue.popleft()
                level.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            result.append(level)
        return result

From Introduction to BFS, with a step-by-step visualizer.

Complexity

O(V + E) time, O(V) space for the queue and visited set. On a grid that is O(rows × cols).

11. Backtracking

Full guide

When to use it

The problem asks for all permutations, all subsets, all combinations, or all ways of doing something — or it is a constraint puzzle like N-Queens or Sudoku. "Find every…" means backtracking.

Template (Python)

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        result = []
        path = []

        def backtrack(start: int) -> None:
            result.append(path[:])
            for i in range(start, len(nums)):
                path.append(nums[i])
                backtrack(i + 1)
                path.pop()

        backtrack(0)
        return result

From Introduction to Backtracking, with a step-by-step visualizer.

Complexity

Exponential by nature — O(2ⁿ) for subsets, O(n!) for permutations. Pruning invalid branches early is the only meaningful optimisation.

12. Graphs (Topological Sort)

Full guide

When to use it

Tasks have prerequisites and you need a valid order, or you need to detect whether the dependencies contain a cycle. Course schedules, build orders and dependency resolution are all this.

Template (Python)

from collections import deque

class Solution:
    def topologicalSort(self, n: int, edges: List[List[int]]) -> List[int]:
        graph = [[] for _ in range(n)]
        indegree = [0] * n
        for u, v in edges:
            graph[u].append(v)
            indegree[v] += 1

        queue = deque(i for i in range(n) if indegree[i] == 0)
        order = []
        while queue:
            u = queue.popleft()
            order.append(u)
            for v in graph[u]:
                indegree[v] -= 1
                if indegree[v] == 0:
                    queue.append(v)

        return order if len(order) == n else []

From Ordering Dependencies (Topological Sort), with a step-by-step visualizer.

Complexity

O(V + E). If the algorithm finishes having emitted fewer than V nodes, the graph contains a cycle — which is how cycle detection falls out for free.

13. Dynamic Programming

Full guide

When to use it

The problem asks for a count of ways, a minimum or maximum over choices, or whether something is achievable — and a brute-force recursion would solve the same subproblem repeatedly. Overlapping subproblems plus optimal substructure is the signature.

Template (Python)

class Solution:
    def fib(self, n: int) -> int:
        memo = {}

        def solve(k: int) -> int:
            if k <= 1:
                return k
            if k in memo:
                return memo[k]
            value = solve(k - 1) + solve(k - 2)
            memo[k] = value
            return value

        return solve(n)

From Intro to Dynamic Programming, with a step-by-step visualizer.

Complexity

Usually O(states × transitions). A 1D DP over n items is O(n); knapsack and most string DP are O(n × m). Space often reduces to one or two rows.

14. Greedy Algorithms

Full guide

When to use it

The problem asks for a maximum or minimum and there is a natural ordering that makes one choice obviously safest — earliest finishing time, largest value first. If a counterexample comes to mind quickly, the problem is dynamic programming instead.

Template (Python)

class Solution:
    def greedyCoins(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        count = 0
        for coin in coins:
            if coin > amount:
                continue
            take = amount // coin
            amount -= take * coin
            count += take
        return count if amount == 0 else -1

From Introduction to Greedy Algorithms, with a step-by-step visualizer.

Complexity

Typically O(n log n) for the sort plus O(n) for a single pass.

15. Trie

Full guide

When to use it

The problem involves prefixes — autocomplete, prefix search, word dictionaries — or repeatedly checks membership of many strings. Grid word-search problems use a trie to prune impossible branches early.

Template (Python)

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word: str) -> None:
        node = self.root
        for char in word:
            if char not in node.children:
                node.children[char] = TrieNode()
            node = node.children[char]
        node.is_end = True

    def startsWith(self, prefix: str) -> bool:
        node = self.root
        for char in prefix:
            if char not in node.children:
                return False
            node = node.children[char]
        return True

From Introduction to Tries (Prefix Trees), with a step-by-step visualizer.

Complexity

O(L) per insert or search, where L is word length, independent of how many words are stored. Space is O(total characters) in the worst case.

16. Prefix Sum

Full guide

When to use it

The problem asks about sums or counts over many subarrays or ranges, especially with repeated queries over unchanging data. "Subarray sum equals k" and "range sum query" are the standard forms.

Template (Python)

class NumArray:
    def __init__(self, nums: List[int]):
        self.P = [0] * (len(nums) + 1)
        for i in range(len(nums)):
            self.P[i + 1] = self.P[i] + nums[i]

    def sumRange(self, left: int, right: int) -> int:
        return self.P[right + 1] - self.P[left]

From Introduction to Prefix Sums, with a step-by-step visualizer.

Complexity

O(n) preprocessing, then O(1) per range query — against O(n) per query when summing directly.

17. Matrices

Full guide

When to use it

The input is a 2D grid and the question involves islands, regions, flood fill, spiral or diagonal traversal, path counting, or rotating and transposing in place.

Complexity

O(rows × cols) for a full traversal. In-place transformations keep space at O(1) beyond the input.

18. Bit Manipulation

Full guide

When to use it

The problem talks about bits, binary representations, powers of two, or asks for O(1) extra space on a counting or "find the missing or single one" task, where XOR cancelling pairs or a bit mask replaces a hash set.

Complexity

O(number of bits) per number, which is O(1) for fixed-width integers, with O(1) extra space — against O(n) extra space for the hash set or array a counting approach would use.