Skip to main content

Welcome to a Grasp lesson

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

Lesson 6

Two-Pointer Technique for Sorted Arrays and Strings

Recommended resources

Hello! Welcome to your sixth lesson.

In our last two lessons, we built a solid foundation in algorithm analysis by learning how to measure both time and space complexity using Big O notation. You now have the tools to evaluate an algorithm's efficiency. Today, we shift from analyzing algorithms to designing them.

This lesson introduces the Two-Pointer Technique, a simple yet remarkably powerful pattern that is a favorite in technical interviews. It's often the key to transforming a slow, brute-force solution into a highly efficient one. You will learn to apply this technique to problems involving sorted arrays and strings, specifically for finding pairs with a target sum and checking for palindromes, which are classic interview scenarios.

Mastering this technique will equip you with a new way of thinking about array and string problems, helping you write code that is not just correct, but also optimal.

1. What is the Two-Pointer Technique?

At its core, the two-pointer technique involves using two integer indices (the "pointers") to traverse a data structure, typically an array or a string, in a single pass. This allows you to process two elements at a time, which can drastically reduce the time complexity compared to nested loops.

While there are several variations, this lesson focuses on the most common one for sorted data: the Opposite Ends Pattern. Here, one pointer starts at the beginning of the data structure, and the other starts at the end. They then move towards each other until they meet or cross.

Two Pointer Algorithm | Two Sum Problem | Solve DS Problems in O(N) Time

Let's begin with a quick overview from the JAVAAID channel. This video explains what the two-pointer technique is and why it's so valuable for optimizing code.

Watch the segment from 00:36 to 03:30. Pay close attention to the core idea of reducing complexity from O(N²) to O(N) and the introduction of the two main variants: 'opposite direction' and 'equal directional'.

To visualize this, imagine an array with a left pointer at index 0 and a right pointer at the last index.

Two Pointer Technique: Solve Array Problems Efficiently
A visual representation of the opposite-ends two-pointer setup on a sorted array. The pointers, `left` and `right`, will move based on the problem's logic.

The pointers move based on conditions derived from the elements they point to, effectively narrowing down the search space with each step.

Two-Pointer Technique Visualization
This GIF demonstrates the pointers moving inward, exploring different pairs of elements in a single pass through the array.

2. Application 1: Pair Sum in a Sorted Array

Let's tackle a classic problem: Given a sorted array of integers, find if there exists a pair of numbers that sum up to a specific target value.

The Brute-Force Approach

The most straightforward solution is to use nested loops. The outer loop picks an element, and the inner loop checks every other element to see if their sum matches the target.

// Brute-force solution
for (int i = 0; i < arr.length; i++) {
    for (int j = i + 1; j < arr.length; j++) {
        if (arr[i] + arr[j] == target) {
            // Found a pair
            return true;
        }
    }
}

Given your understanding of complexity analysis, you can see this is an O(n^2) time complexity solution. For large arrays, this is too slow. We can do much better.

The Two-Pointer Optimization

Since the array is sorted, we can leverage that property. This is the key insight for applying the two-pointer technique.

  1. Initialize left = 0 and right = arr.length - 1.
  2. Calculate currentSum = arr[left] + arr[right].
  3. Compare currentSum with target:
    • If currentSum == target, we've found our pair.
    • If currentSum < target, we need a larger sum. Since the array is sorted, the only way to increase the sum is to move the left pointer one step to the right (left++).
    • If currentSum > target, we need a smaller sum. The only way to decrease the sum is to move the right pointer one step to the left (right--).
  4. Repeat step 2 and 3 until left >= right. If the loop finishes without finding a pair, none exists.

Two Pointer Algorithm | Two Sum Problem | Solve DS Problems in O(N) Time

The JAVAAID video provides an excellent step-by-step walkthrough of this exact logic with a clear example.

Watch from 04:29 to 11:33. The video will first walk through an example visually (04:29-08:20), then show the Java implementation (09:26-11:09), and finally analyze the complexity (11:09-11:33).

This approach eliminates the nested loop. We make a single pass through the array, moving the pointers inwards. This reduces the time complexity to O(n) with O(1) auxiliary space, a massive improvement and the optimal solution expected in an interview.

Test your understanding!

Given a sorted array nums and a target k, how would you modify the two-pointer algorithm to count the total number of pairs that sum up to k? Consider the case where duplicate numbers might exist, e.g., [2, 2, 5, 5] with k=7.

Show answer

When you find a pair (nums[left], nums[right]) that sums to k, you can't just move one pointer. If nums[left] and nums[right] are unique, you can increment left and decrement right and continue.

However, if there are duplicates, you need to count how many times nums[left] appears and how many times nums[right] appears. Let's say nums[left] appears countLeft times and nums[right] appears countRight times. The number of pairs you can form is countLeft * countRight. After counting, you'd move the left pointer past all its duplicates and the right pointer past all its duplicates.

A special case is when nums[left] == nums[right] (and 2 * nums[left] == k). The number of pairs is C(count, 2) = count * (count - 1) / 2, where count is the number of occurrences of that number.


3. Application 2: Palindrome Check

The two-pointer technique isn't limited to numbers; it's also highly effective for string manipulation. A classic example is checking if a string is a palindrome.

A palindrome is a word, phrase, or sequence that reads the same backward as forward. For interview problems, this often comes with a twist: you must first convert the string to lowercase and consider only alphanumeric characters (letters and numbers).

For example, "A man, a plan, a canal: Panama" becomes "amanaplanacanalpanama", which is a palindrome.

The Two-Pointer Approach for Palindromes

  1. (Preprocessing) Create a "cleaned" version of the string, or iterate through the original string while skipping non-alphanumeric characters.
  2. Initialize left = 0 and right = cleanedString.length() - 1.
  3. In a loop, while left < right:
    • Compare the characters at cleanedString.charAt(left) and cleanedString.charAt(right).
    • If they are not the same, the string is not a palindrome. Return false.
    • If they are the same, move the pointers closer: left++ and right--.
  4. If the loop completes without finding any mismatches, the string is a palindrome. Return true.

Valid Palindrome | LeetCode problem 125 | Top 150 interview question series

The Technosage channel has a clear video that breaks down this exact problem (LeetCode 125 - Valid Palindrome).

Watch the segment from 00:00 to 04:51. This covers the problem definition (00:00-01:45), the two-pointer logic (01:55-03:15), and a complete Java implementation (03:15-04:51).

This algorithm has a time complexity of O(n) because we iterate through the string at most once. The space complexity depends on the preprocessing step. If you create a new cleaned string, it's O(n). If you use pointers on the original string and skip non-alphanumeric characters within the loop, you can achieve O(1) auxiliary space.


4. Pattern Recognition and Pro Tips

Recognizing when to use this technique is a key skill for interviews. The dev.to article below provides excellent guidance on this.

Two Pointer Technique: Mastering the Opposite Ends Pattern

To solidify your understanding, please read selected parts of the article 'Two Pointer Technique: Mastering the Opposite Ends Pattern' from dev.to. It summarizes the pattern and gives great interview-centric advice.

Please read two sections: Pattern Recognition 🔍: This section gives you a checklist of problem types where you should immediately think of using this pattern. Common Pitfalls & Tips ⚠️: This section provides crucial advice for avoiding common mistakes during implementation, which is invaluable for your interview preparation.

Here are the key situations where you should consider the opposite-ends two-pointer pattern:

  • The input is a sorted array or you can afford to sort it (O(n log n)).
  • You are looking for a pair of elements that satisfy a certain condition (e.g., sum, difference).
  • You need to check for palindromes in a string.
  • The problem involves finding a subarray or subsequence that has some property related to its ends.

Conclusion

Today, you've added a fundamental algorithmic pattern to your toolkit. The two-pointer technique is a prime example of how leveraging data properties (like a sorted array) can lead to significant performance gains. It's a testament to the idea that a clever algorithm is often more powerful than raw computing power.

Key Takeaways:

  • The Two-Pointer Technique uses two indices to process a data structure in a single pass, often optimizing a brute-force O(n^2) solution to O(n).
  • The Opposite Ends Pattern is ideal for sorted arrays and strings, with one pointer at the start and one at the end, moving towards each other.
  • Pair Sum Problem: In a sorted array, moving the left pointer increases the sum, and moving the right pointer decreases it, allowing for an efficient search.
  • Palindrome Check: Comparing characters from both ends of a string inwards is an elegant and efficient way to verify if it's a palindrome.

Preview of the next lesson:
We will continue exploring pointer-based algorithms in our next lesson by looking at the Sliding Window Technique. Like the two-pointer method, it uses two pointers, but they typically move in the same direction to define a dynamic "window" over a portion of the data. It's another essential pattern for solving a different class of array and string problems efficiently.