Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 11

Two-Pointer Technique for Arrays and Strings

Recommended resources

Hello! Welcome to the first lesson in our module on "High-ROI Interview Patterns." This module is designed to equip you with the most effective and frequently tested algorithmic patterns that you'll encounter in technical interviews.

In our last lesson on strings, we had a brief preview of the two-pointer technique when we solved the "Valid Palindrome" problem. Today, we will formalize this powerful pattern. Our goal is to apply the two-pointer technique to solve a variety of array and string problems, focusing on the common "converging" or "opposite ends" variant. Mastering this pattern is a huge step forward in your interview preparation, as it often turns a slow, brute-force O() solution into an optimal O() one.

1. What is the Two-Pointer Technique?

At its core, the two-pointer technique involves using two integer variables—pointers—that move through a data structure, typically an array or string. By coordinating their movement, we can process data or find elements that satisfy a condition much more efficiently than with nested loops.

There are a few common ways this pattern is applied, which you'll see throughout this module. To get a high-level overview of these variations, let's start with a short, conceptual video.

Two Pointers in 7 minutes | LeetCode Pattern

The video 'Two Pointers in 7 minutes' by AlgoMasterIO provides an excellent breakdown of the three main strategies for this pattern.

Watch from 00:27 to 02:27. Focus on understanding the distinction between the three strategies mentioned: converging pointers, parallel pointers, and trigger-based pointers. Today's lesson will concentrate on the first one.

As the video explained, our focus today is on the converging pointers (or "opposite ends") pattern. This is where we initialize one pointer at the beginning of the data structure and another at the end, then move them toward each other until they meet or cross.


2. The Canonical Example: Two Sum in a Sorted Array

The most classic application of the converging pointers pattern is solving the "Two Sum" problem on a sorted array. This problem is a fundamental building block for many harder interview questions.

Problem: Given an array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number. Return the indices of the two numbers.

A brute-force approach would use nested loops to check every possible pair, resulting in an O() time complexity. We can do much better.

The key insight comes from the array being sorted.

  1. Initialize a left pointer at index 0 and a right pointer at the last index.
  2. Calculate the current_sum = numbers[left] + numbers[right].
  3. Compare current_sum with the target:
    • If current_sum < target, we need a larger sum. Since the array is sorted, we must move our left pointer one step to the right to include a larger number.
    • If current_sum > target, we need a smaller sum. We must move our right pointer one step to the left to include a smaller number.
    • If current_sum == target, we have found our pair!

This process efficiently eliminates possibilities with each step, allowing us to find the solution in a single pass.

Two-Pointer Technique for Target Sum
This image visualizes the logic for the Two Sum problem on a sorted array. The pointers converge based on whether the current sum is smaller or larger than the target.

To see this implemented in code and explained in detail, please read the following section from the CodeBeast article.

Cracking the Two Pointers Pattern

The article 'Cracking the Two Pointers Pattern' provides an excellent walkthrough of the Two Sum problem, comparing the brute-force method to the optimal two-pointer solution.

Read the section titled 'Problem Walkthrough', focusing on the subsections 'Two Sum' and 'The best solution (Two Pointer Technique)'. Pay close attention to the logic explained with the diagram and the Python code implementation.

This O() time and O(1) space solution is exactly what interviewers are looking for.


3. Pattern Recognition: When Should You Think "Two Pointers"?

Recognizing when to apply a pattern is just as important as knowing how to implement it. So, what are the tell-tale signs that a problem might be solvable with the two-pointer technique?

Here are some common clues:

  • The input is a sorted array or string.
  • The problem involves finding a pair of elements that satisfy a certain condition (e.g., a target sum).
  • A brute-force solution would involve nested loops to compare every pair of elements.
  • The problem requires an in-place modification or has a strict O(1) space complexity constraint.

The next video gives more examples and summarizes these clues effectively.

Coding Interview Patterns - Two Pointers

The video 'Coding Interview Patterns - Two Pointers' from Nikhil Lohia expands on this idea of pattern recognition.

Watch the segment from 07:48 to 10:13. The presenter discusses keywords and problem characteristics that suggest using two pointers. Try to connect these clues to the Two Sum problem we just discussed.

As a supplement, the CodeBeast article you read earlier also has a fantastic section on this topic. It's worth a quick review to solidify these concepts.

Cracking the Two Pointers Pattern

Let's revisit the 'Cracking the Two Pointers Pattern' article for a structured breakdown of pattern recognition.

Quickly read the section 'Pattern Breakdown and Recognition', specifically the part 'When to Use Two Pointers?'. This provides a concise checklist.


4. Other Applications of Converging Pointers

The same "opposite ends" logic applies to many other problems.

Valid Palindrome (Recap)

In the last lesson, we checked if a string is a palindrome. This is a perfect example of the converging pointers pattern.

  1. Pointers start at the beginning and end of the string.
  2. They move inward, skipping non-alphanumeric characters.
  3. At each step, they check if the characters are equal (ignoring case). If not, it's not a palindrome.
  4. If the pointers meet or cross without finding a mismatch, it is a palindrome.
Two-Pointer Palindrome Check Visualization
A visualization of the 's' (start) and 'e' (end) pointers converging to check for a palindrome.

Squares of a Sorted Array

Here's a less obvious but common interview problem that can be optimized with two pointers.

Problem: Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order.

Example: Input: nums = [-4,-1,0,3,10], Output: [0,1,9,16,100]

A simple approach is to square every element and then sort the resulting array. This would be O(n log n). Can we do it in O(n)?

Yes! The largest squared values will come from the numbers at the ends of the original array (because of negative numbers). For [-4, -1, 0, 3, 10], the largest square could be (-4)^2 = 16 or 10^2 = 100.

This insight leads to a two-pointer solution:

  1. Initialize left = 0, right = len(nums) - 1.
  2. Create a result array of the same size, filled with zeros.
  3. Use a third pointer, write_pos, starting from the end of the result array (len(nums) - 1).
  4. While left <= right:
    • Compare abs(nums[left]) and abs(nums[right]).
    • Whichever absolute value is larger will produce the larger square. Place this square at result[write_pos].
    • Move the pointer (left or right) that corresponded to the larger absolute value.
    • Decrement write_pos.
  5. Return the result array.
Test your understanding!

Given the input nums = [-7, -3, 2, 3, 11] and using the two-pointer method for "Squares of a Sorted Array," what is the first number that will be placed into the result array, and at which index?

Show answer
  1. left = 0 (points to -7), right = 4 (points to 11).
  2. abs(-7) is 7, abs(11) is 11.
  3. Since 11 > 7, the larger square will be 11*11 = 121.
  4. This value is placed at the end of the result array.

So, the first number placed is 121 at index 4 of the result array. The right pointer would then be decremented.


Conclusion

You've now formally learned one of the most fundamental interview patterns. By replacing nested loops with two intelligently moving pointers, you can write clean, efficient, and optimal code.

Key Takeaways:

  • The Two-Pointer Technique is a high-ROI pattern for optimizing problems on sequential data structures like arrays and strings, typically from O() to O().
  • The Converging (Opposite Ends) Pattern is ideal for sorted arrays where you need to find pairs satisfying a condition (like a target sum) or for problems involving symmetry (like palindromes).
  • Pattern recognition is crucial: Look for sorted data, pair-finding requirements, and opportunities to avoid nested loops.

In our next lesson, we will explore a different variation of this pattern: the fast and slow pointer technique. It's another powerful tool, used most famously to solve problems involving cycles and midpoints in linked lists.