Core Java

Master LeetCode with These 10 Proven Patterns

LeetCode is a popular platform for practicing coding problems and preparing for technical interviews. While many find LeetCode challenging, understanding common problem-solving patterns can significantly improve your ability to tackle these problems effectively. This article will introduce you to 10 proven patterns that can help you master LeetCode and become a more proficient programmer.

By familiarizing yourself with these patterns, you’ll gain a deeper understanding of problem-solving techniques, develop a more systematic approach to coding, and increase your confidence in tackling complex LeetCode challenges.

Let’s Master LeetCode with These 10 Proven Patterns

leetcode logo

1. Two Pointers

Definition: The two-pointers pattern involves using two pointers to iterate through a data structure, often an array or linked list, to solve problems by narrowing down the search space.

Common Use Cases:

  • Finding pairs of elements that satisfy a condition.
  • Merging sorted arrays or linked lists.
  • Validating palindromes.

Example:
Problem: Given a sorted array of integers, find the pair of elements that sum to a given target value.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public int[] twoSum(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left < right) {
        int currentSum = nums[left] + nums[right];
        if (currentSum == target) {
            return new int[] { left, right };
        } else if (currentSum < target) {
            left++;
        } else {
            right--;
        }
    }
    return new int[] {}; // Return empty if no pair is found
}

2. Sliding Window

Definition: The sliding window pattern involves maintaining a window of a fixed or variable size that slides over the data structure to solve problems involving subarrays or substrings.

Common Use Cases:

  • Finding maximum or minimum subarrays.
  • Counting occurrences of elements within a window.

Example:
Problem: Given an array of integers and a window size, find the maximum sum of a subarray of that size.

01
02
03
04
05
06
07
08
09
10
11
12
13
public int maxSumSubarray(int[] nums, int k) {
    int maxSum = 0, windowSum = 0;
    for (int i = 0; i < k; i++) {
        windowSum += nums[i];
    }
    maxSum = windowSum;
 
    for (int i = k; i < nums.length; i++) {
        windowSum += nums[i] - nums[i - k];
        maxSum = Math.max(maxSum, windowSum);
    }
    return maxSum;
}

3. Binary Search

Definition: The binary search pattern is used to efficiently search for a target value in a sorted array or list by dividing the array in half repeatedly.

Common Use Cases:

  • Searching for a specific element in a sorted array.
  • Finding the first or last occurrence of an element.

Example:
Problem: Given a sorted array of integers and a target value, find the index of the target value in the array.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1; // Return -1 if target not found
}

4. Divide and Conquer

Definition: Divide and conquer involves breaking down a problem into smaller subproblems, solving the subproblems recursively, and combining the solutions to solve the original problem.

Common Use Cases:

  • Sorting algorithms (e.g., merge sort, quicksort).
  • Searching algorithms (e.g., binary search).

Example:
Problem: Implement merge sort to sort an array of integers.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public void mergeSort(int[] arr, int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}
 
private void merge(int[] arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
 
    int[] L = new int[n1];
    int[] R = new int[n2];
 
    for (int i = 0; i < n1; ++i) {
        L[i] = arr[left + i];
    }
    for (int j = 0; j < n2; ++j) {
        R[j] = arr[mid + 1 + j];
    }
 
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }
 
    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }
 
    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }
}

5. Dynamic Programming

Definition: Dynamic programming involves storing the results of subproblems to avoid recomputation.

Common Use Cases:

Example:
Problem: Given a staircase with n steps, find the number of distinct ways to climb to the top, given that you can take either 1 or 2 steps at a time.

01
02
03
04
05
06
07
08
09
10
public int climbStairs(int n) {
    if (n <= 2) return n;
    int[] dp = new int[n + 1];
    dp[1] = 1;
    dp[2] = 2;
    for (int i = 3; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

6. Backtracking

Definition: Backtracking involves exploring all possible solutions and discarding those that don’t lead to the desired solution.

Common Use Cases:

  • Solving puzzles (e.g., Sudoku, N-Queens).
  • Generating permutations and combinations.

Example:
Problem: Solve the N-Queens problem for an n x n chessboard.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public List<List<String>> solveNQueens(int n) {
    List<List<String>> solutions = new ArrayList<>();
    char[][] board = new char[n][n];
    for (char[] row : board) {
        Arrays.fill(row, '.');
    }
    solve(0, board, solutions);
    return solutions;
}
 
private void solve(int col, char[][] board, List<List<String>> solutions) {
    if (col == board.length) {
        solutions.add(construct(board));
        return;
    }
    for (int row = 0; row < board.length; row++) {
        if (isValid(board, row, col)) {
            board[row][col] = 'Q';
            solve(col + 1, board, solutions);
            board[row][col] = '.';
        }
    }
}
 
private boolean isValid(char[][] board, int row, int col) {
    for (int i = 0; i < col; i++) {
        if (board[row][i] == 'Q') return false;
    }
    for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q') return false;
    }
    for (int i = row, j = col; i < board.length && j >= 0; i++, j--) {
        if (board[i][j] == 'Q') return false;
    }
    return true;
}
 
private List<String> construct(char[][] board) {
    List<String> path = new ArrayList<>();
    for (char[] row : board) {
        path.add(new String(row));
    }
    return path;
}

7. Greedy Algorithm

Definition: The greedy algorithm pattern involves making locally optimal choices at each step, hoping to arrive at a globally optimal solution.

Common Use Cases:

  • Activity selection problem.
  • Dijkstra’s algorithm for shortest path.

Example:
Problem: Given a set of activities with start and finish times, find the maximum number of activities that can be scheduled without overlapping.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public int maxActivities(int[] start, int[] finish) {
    int n = start.length;
    int[][] activities = new int[n][2];
    for (int i = 0; i < n; i++) {
        activities[i][0] = start[i];
        activities[i][1] = finish[i];
    }
    Arrays.sort(activities, Comparator.comparingInt(a -> a[1]));
 
    int count = 1;
    int lastFinishTime = activities[0][1];
 
    for (int i = 1; i < n; i++) {
        if (activities[i][0] >= lastFinishTime) {
            count++;
            lastFinishTime = activities[i][1];
        }
    }
    return count;
}

8. Two Pointers (Reverse)

Definition: This pattern is similar to the Two Pointers pattern but involves moving the pointers in opposite directions.

Common Use Cases:

  • Reversing arrays or linked lists.
  • Finding pairs of elements that satisfy a certain condition.

Example:
Problem: Given an array of integers, reverse the array in place.

01
02
03
04
05
06
07
08
09
10
public void reverseArray(int[] arr) {
    int left = 0, right = arr.length - 1;
    while (left < right) {
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
        left++;
        right--;
    }
}

9. Hash Table

Definition: The hash table pattern involves using a hash table to store key-value pairs efficiently.

Common Use Cases:

  • Checking for duplicates in an array.
  • Implementing frequency counters.

Example:
Problem: Given an array of integers, find the two numbers that add up to a given target value.

01
02
03
04
05
06
07
08
09
10
11
public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[] { map.get(complement), i };
        }
        map.put(nums[i], i);
    }
    return new int[] {}; // Return empty if no pair found
}

10. Graph Algorithms

Definition: Graph algorithms solve problems on graphs (e.g., directed or undirected).

Common Use Cases:

  • Breadth-first search (BFS) for finding shortest paths.
  • Depth-first search (DFS) for exploring graphs.

Example:
Problem: Given a graph, find all connected components using DFS.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
public void dfs(int node, List<List<Integer>> graph, boolean[] visited) {
    visited[node] = true;
    for (int neighbor : graph.get(node)) {
        if (!visited[neighbor]) {
            dfs(neighbor, graph, visited);
        }
    }
}
 
public List<List<Integer>> findConnectedComponents(int n, List<List<Integer>> graph) {
    boolean[] visited = new boolean[n];
    List<List<Integer>> components = new ArrayList<>();
 
    for (int i = 0; i < n; i++) {
        if (!visited[i]) {
            List<Integer> component = new ArrayList<>();
            dfs(i, graph, visited);
            components.add(component);
        }
    }
    return components;
}

Conclusion

In this article, we explored 10 proven problem-solving patterns essential for mastering LeetCode and improving algorithmic thinking. These patterns—such as Two Pointers, Sliding Window, Binary Search, Divide and Conquer, and others—are widely applicable to a variety of coding problems. Understanding these patterns helps in breaking down complex challenges into simpler steps, enabling the efficient development of solutions.

Each pattern was accompanied by a Java code example, demonstrating how to implement these strategies in real-world scenarios. Whether you’re tackling problems involving arrays, graphs, or dynamic programming, recognizing the appropriate pattern can significantly enhance your problem-solving speed and accuracy. Mastering these patterns is key to excelling in technical interviews and becoming a stronger software engineer.

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button