Top Leetcode patterns and how to approach

Duc Nguyen HuuDuc Nguyen Huu
13 min read

Preparing for coding interviews can be challenging, but recognizing common patterns in problems can make it easier. LeetCode is a popular platform where you can practice these patterns. By understanding and mastering these patterns, you can solve a wide range of problems more efficiently. In this article, we will discuss some of the top LeetCode patterns and how to approach them.

Key Takeaways

  • Understanding common patterns in coding problems can significantly improve problem-solving skills.
  • LeetCode is a valuable resource for practicing coding problems and recognizing patterns.
  • The Two Pointers technique is useful for solving problems involving arrays and strings.
  • The Sliding Window pattern helps in solving problems related to subarrays or substrings.
  • Dynamic Programming is a powerful technique for solving problems with overlapping subproblems and optimal substructure.

1. Two Pointers

The Two Pointers technique is a simple yet powerful approach used in many array and string problems. This method involves using two pointers to iterate through an array or list, often to find pairs or elements that meet specific criteria.

To solve problems with sorted arrays that require fulfilling certain constraints with a specific set of elements, the two pointers technique becomes quite handy. The set of elements could be a pair, a triplet, or even a subarray.

How It Works

  1. Initialize two pointers: One at the start (left) and one at the end (right) of the array.
  2. Check the sum: See if the numbers pointed by the two pointers add up to the target sum.
  3. Move pointers: If their sum is smaller than the target, shift the left pointer to the right. If their sum is greater, shift the right pointer to the left.
  4. Return the result: If the sum equals the target, return the indices or values pointed out by the two pointers.

Example Problem

Find two numbers in a sorted array that add up to a target value.

Input: nums = [1, 2, 3, 4, 6], target = 6

Output: [1, 3]

Steps to Solve

  1. Initialize two pointers, one at the start and one at the end of the array.
  2. Check the sum of the elements at the two pointers.
  3. If the sum equals the target, return the indices.
  4. If the sum is less than the target, move the left pointer to the right.
  5. If the sum is greater than the target, move the right pointer to the left.

Tip: This technique is especially useful for problems involving sorted arrays or lists where you need to find pairs that satisfy a specific condition.

2. Sliding Window

The Sliding Window pattern is a useful technique for solving problems that involve finding a subarray or substring that meets certain conditions. This method helps optimize time complexity by maintaining a window of elements.

How It Works

  1. Start with the sum of the first k elements.
  2. Slide the window one element at a time, subtracting the element that goes out of the window and adding the new element.
  3. Keep track of the maximum sum encountered.

Example Problem

Find the maximum sum of a subarray of size k.

Input: nums = [2, 1, 5, 1, 3, 2], k = 3

Output: 9

Explanation

  1. Calculate the sum of the first k elements: 2 + 1 + 5 = 8.
  2. Slide the window to include the next element and exclude the first: 1 + 5 + 1 = 7.
  3. Continue this process and keep track of the maximum sum, which is 9 in this case.

The sliding window technique can be divided into fixed sliding window problems and dynamic sliding window problems.

LeetCode Problems

  • Maximum Average Subarray I (LeetCode #643)
  • Longest Substring Without Repeating Characters (LeetCode #3)
  • Minimum Window Substring (LeetCode #76)

3. 0/1 Knapsack

The 0/1 Knapsack problem is a classic example of dynamic programming. The task is to put the items into the bag such that the sum of profits associated with them is the maximum possible. Given the weights and profits of 'N' items, you need to determine the most profitable subset of items that can fit into a knapsack of capacity 'C'. Each item can either be included or excluded from the knapsack.

Let's consider an example with fruits:

ItemsWeightsProfits
Apple24
Orange35
Banana13
Melon47

The knapsack has a capacity of 5. Here are some possible combinations:

  • Apple + Orange (total weight 5) => 9 profit
  • Apple + Banana (total weight 3) => 7 profit
  • Orange + Banana (total weight 4) => 8 profit
  • Banana + Melon (total weight 5) => 10 profit

As you can see, the combination of Banana and Melon yields the highest profit without exceeding the maximum weight.

To solve this problem, you can start with a brute-force recursive solution to understand the overlapping subproblems. After that, you can use advanced techniques like Memoization and Bottom-Up Dynamic Programming to optimize the solution.

Here's a simple brute-force algorithm:

  1. For each item 'i', create a new set that includes item 'i' if the total weight does not exceed the capacity, and recursively process the remaining capacity and items.
  2. Create a new set without item 'i', and recursively process the remaining items.
  3. Return the set with the higher profit from the above two sets.

This approach has a time complexity of O(2^n).

By learning the patterns behind common questions, you can answer any interview problem more effectively.

4. Fast and Slow Pointers

The fast and slow pointers pattern involves using two pointers that move through a data structure at different speeds. This technique is often called the Hare and Tortoise algorithm. It's particularly useful for detecting cycles in linked lists.

To implement this, you create two pointers: a slow pointer and a fast pointer. In each step, the slow pointer moves one step, while the fast pointer moves two steps.

Key Scenarios

  1. If the linked list has no cycle, the fast pointer will reach the end before the slow pointer.
  2. If the linked list has a cycle, the fast pointer will eventually catch up to the slow pointer within the cycle.

Steps to Detect a Cycle

  1. Initialize two pointers, slow and fast, at the head of the linked list.
  2. Move the slow pointer one step and the fast pointer two steps in each iteration.
  3. If the fast pointer reaches the end, the list has no cycle.
  4. If the fast pointer meets the slow pointer, a cycle exists.

This method is efficient with a time complexity of O(n), where n is the number of nodes in the linked list.

5. Merge Intervals

Computer screen with code and coffee cup

The Merge Intervals pattern is a technique to handle overlapping intervals. A simple approach is to start from the first interval and compare it with all other intervals for overlapping. If it overlaps with any other interval, then merge them.

Steps to Merge Intervals

  1. Sort the intervals by their start time.
  2. Create an empty list called merged to store the merged intervals.
  3. Iterate through the intervals and check if it overlaps with the last interval in the merged list.
  4. If it overlaps, merge the intervals by updating the end time of the last interval in merged.
  5. If it does not overlap, simply add the current interval to the merged list.

Example

  • Input: intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
  • Output: [[1, 6], [8, 10], [15, 18]]

Explanation

  1. Sort the intervals by their start time: [[1, 3], [2, 6], [8, 10], [15, 18]]
  2. Initialize merged as an empty list.
  3. Iterate through the sorted intervals:
    • Compare [1, 3] with the last interval in merged (none in this case), so add [1, 3] to merged.
    • Compare [2, 6] with [1, 3] in merged, they overlap, so merge to [1, 6].
    • Compare [8, 10] with [1, 6] in merged, they do not overlap, so add [8, 10] to merged.
    • Compare [15, 18] with [8, 10] in merged, they do not overlap, so add [15, 18] to merged.

LeetCode Problems

  • Merge Intervals (LeetCode #56)
  • Insert Interval (LeetCode #57)
  • Non-Overlapping Intervals (LeetCode #435)

6. In-Place Reversal of a Linked List

In many coding interview problems, you might be asked to reverse the links between nodes in a linked list. The challenge often comes from doing this in-place, meaning you can't use extra memory.

To reverse a linked list in place, follow these steps:

  1. Initialize three pointers: previous, current, and next.
  2. Traverse the linked list.
  3. During each step, adjust the pointers to reverse the direction of the links.
  4. Continue until you reach the end of the list.

This method uses O(1) space and allows you to reverse the linked list in a single traversal. The worst-case time complexity is O(N), where N is the number of nodes in the list.

This pattern is especially useful when you need to reverse sections of a linked list without using extra space.

Example Problem

Reverse a sublist of a linked list from position m to n.

Input: head = [1, 2, 3, 4, 5], m = 2, n = 4

Output: [1, 4, 3, 2, 5]

Explanation:

  1. Identify the start and end of the sublist.
  2. Reverse the nodes in place by adjusting the pointers.

LeetCode Problems

  1. Reverse Linked List (LeetCode #206)
  2. Reverse Linked List II (LeetCode #92)
  3. Swap Nodes in Pairs (LeetCode #24)

7. Tree Breadth-First Search (BFS)

Team brainstorming tree diagrams on whiteboard

The Breadth-First Search (BFS) algorithm is used to explore and search tree or graph data structures. All nodes at the current depth are explored before moving to the next level. This method is typically implemented using a queue.

Steps of the BFS Algorithm

  1. Start by adding the root node to the queue.
  2. Continue until the queue is empty.
  3. In each iteration, count the elements in the queue (let's call this levelSize). This represents the number of nodes at the current level.
  4. Remove levelSize nodes from the queue and add their values to an array representing the current level.
  5. For each node removed, add its children to the queue.
  6. Repeat step 3 for the next level if the queue is not empty.

BFS is particularly useful for problems that require level order traversal of a tree.

Example Problem: Level Order Traversal

Given a binary tree, perform a level order traversal.

Input: root = [3, 9, 20, null, null, 15, 7]

Output: [[3], [9, 20], [15, 7]]

Explanation

  1. Use a queue to keep track of nodes at each level.
  2. Traverse each level and add the children of the current nodes to the queue.

LeetCode Problems

  • Binary Tree Level Order Traversal (LeetCode #102)
  • Rotting Oranges (LeetCode #994)
  • Word Ladder (LeetCode #127)

8. Prefix Sum

The prefix sum pattern involves preprocessing an array to create a new array where each element at index i represents the sum of the array from the start up to i. This allows for efficient sum queries on subarrays.

When to Use

Use this pattern when you need to perform multiple sum queries on a subarray or need to calculate cumulative sums.

Example Problem

Given an array nums, answer multiple queries about the sum of elements within a specific range [i, j].

Example:

  • Input: nums = [1, 2, 3, 4, 5, 6], i = 1, j = 3
  • Output: 9

Explanation:

  1. Preprocess the array A to create a prefix sum array: P = [1, 3, 6, 10, 15, 21].
  2. To find the sum between indices i and j, use the formula: P[j] - P[i-1].

LeetCode Problems

  1. Range Sum Query - Immutable (LeetCode #303)
  2. Contiguous Array (LeetCode #525)
  3. Subarray Sum Equals K (LeetCode #560)

Prefix sum involves preprocessing an array to create a new array where each element at index i represents the sum of the array from the start up to i.

9. Dynamic Programming

Dynamic Programming (DP) is a method used to solve complex problems by breaking them down into simpler subproblems. It is especially useful for problems with overlapping subproblems and optimal substructure. DP can be approached in two main ways: bottom-up and top-down.

Key Patterns in Dynamic Programming

Some of the most important DP patterns include:

  • Fibonacci Numbers
  • 0/1 Knapsack
  • Longest Common Subsequence (LCS)
  • Longest Increasing Subsequence (LIS)
  • Subset Sum
  • Matrix Chain Multiplication

Example Problem: Fibonacci Numbers

To calculate the n-th Fibonacci number, you can use a bottom-up approach. Start with the first two numbers (0 and 1) and iterate to calculate the next numbers using the formula dp[i] = dp[i - 1] + dp[i - 2].

LeetCode Problems

Here are some LeetCode problems to practice Dynamic Programming:

  1. Climbing Stairs (LeetCode #70)
  2. House Robber (LeetCode #198)
  3. Coin Change (LeetCode #322)
  4. Longest Common Subsequence (LCS) (LeetCode #1143)
  5. Longest Increasing Subsequence (LIS) (LeetCode #322)
  6. Partition Equal Subset Sum (LeetCode #416)

For more Dynamic Programming Patterns, check out my other article: [20 patterns to master dynamic programming](#).

10. Matrix Traversal

Matrix traversal involves moving through a matrix (or 2D grid) using various techniques like Depth-First Search (DFS) and Breadth-First Search (BFS). This pattern is useful for problems where you need to explore elements in a matrix either horizontally, vertically, or diagonally.

Sample Problem

Perform a flood fill on a 2D grid. Change all the cells connected to the starting cell to a new color.

Example:

  • Input: image = [[1,1,1],[1,1,0],[1,0,1]], sr = 1, sc = 1, newColor = 2
  • Output: [[2,2,2],[2,2,0],[2,0,1]]

Explanation:

  1. Use DFS or BFS to traverse the matrix starting from the given cell.
  2. Change the color of the connected cells to the new color.

LeetCode Problems

  1. Flood Fill (LeetCode #733)
  2. Number of Islands (LeetCode #200)
  3. Surrounded Regions (LeetCode #130)

Unleash your coding prowess with this DSA graph common question patterns cheatsheet! From traversing graphs to finding shortest paths and solving connectivity issues, mastering matrix traversal can significantly boost your problem-solving skills.

Conclusion

Mastering LeetCode patterns is like learning the secret codes to unlock a treasure chest of coding problems. Instead of getting lost in a sea of random questions, focusing on these patterns helps you see the bigger picture. By understanding techniques like Two Pointers, Sliding Windows, and Dynamic Programming, you can tackle a wide range of problems with confidence. Remember, it's not about solving as many problems as possible, but about recognizing the patterns and applying them effectively. Keep practicing, stay curious, and soon you'll find yourself solving even the toughest coding challenges with ease. Happy coding!

Frequently Asked Questions

What is LeetCode?

LeetCode is a website where you can practice coding problems that are often asked in job interviews. It has over 2,000 problems that cover various topics and difficulty levels.

Why should I focus on patterns when practicing LeetCode problems?

Focusing on patterns helps you understand the underlying techniques used to solve a wide range of problems. This makes it easier to tackle new problems that you haven't seen before.

What is the Two Pointers technique?

The Two Pointers technique involves using two pointers to iterate through an array or list. It is often used to solve problems related to sorting, searching, and linked lists.

How does the Sliding Window technique work?

The Sliding Window technique involves creating a window of elements in an array or list and sliding it over to find a solution. This is useful for problems that require finding subarrays or substrings.

Can you explain the 0/1 Knapsack pattern?

The 0/1 Knapsack pattern is a type of dynamic programming problem where you decide whether to include items in a knapsack to maximize the total value without exceeding the weight limit.

What is the Fast and Slow Pointers technique?

The Fast and Slow Pointers technique uses two pointers that move at different speeds. It is commonly used to detect cycles in linked lists and solve other problems related to sequences.

0
Subscribe to my newsletter

Read articles from Duc Nguyen Huu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Duc Nguyen Huu
Duc Nguyen Huu