DEV Community

Elxpro
Elxpro

Posted on Originally published at alchemistdrops.com

Mastering Two Pointers: A Step-by-Step Guide to Solving Sequence Problems

Mastering Two Pointers: A Step-by-Step Guide to Solving Sequence Problems

Two Pointers is an algorithmic pattern used to solve problems by tracking two positions in a sequence at the same time.

A sequence can be:

  • an array;
  • a list;
  • a string;
  • a linked list.

Instead of checking every possible combination with nested loops, the Two Pointers pattern moves two pointers through the data using a clear rule.

This often improves a solution from O(n^2) to O(n).

What Kind of Problems Does Two Pointers Solve?

Two Pointers is useful when a problem asks you to work with two positions inside the same sequence.

It commonly solves problems like:

  • finding two numbers that match a condition;
  • checking if a string is a palindrome;
  • reversing a string or list;
  • moving values inside an array;
  • removing duplicates from a sorted array;
  • comparing values from both ends;
  • working with sorted arrays;
  • detecting cycles in linked lists.

The most important idea is:

Every time you move a pointer, you must know why that move is safe.

The Core Idea

A Two Pointers solution usually follows this structure:

  1. Create a left pointer.
  2. Create a right pointer.
  3. Compare the values at both pointers.
  4. Decide which pointer should move.
  5. Repeat until you find the answer or the pointers meet.

Example:

[2, 7, 11, 15]
 L          R
Enter fullscreen mode Exit fullscreen mode

Here:

  • L points to the beginning;
  • R points to the end.

When Should You Think About Two Pointers?

Ask yourself these questions:

  1. Is the input a sequence?
  2. Do I need to compare two values?
  3. Is the sequence sorted?
  4. Am I looking for a pair?
  5. Do I need to reverse something?
  6. Do I need to move values in-place?
  7. Can I discard part of the search space after each comparison?

If the answer is yes to some of these questions, Two Pointers may be useful.

Common Two Pointers Strategies

1. Opposite Direction Pointers

One pointer starts at the beginning.

The other pointer starts at the end.

[1, 2, 3, 4, 5]
 L           R
Enter fullscreen mode Exit fullscreen mode

This is useful for:

  • palindrome checks;
  • reversing arrays;
  • sorted Two Sum;
  • container with most water.

2. Same Direction Pointers

Both pointers move from left to right.

[0, 1, 0, 3, 12]
 R
 W
Enter fullscreen mode Exit fullscreen mode

Usually:

  • one pointer reads values;
  • the other pointer writes or tracks valid positions.

This is useful for:

  • moving zeroes;
  • removing duplicates;
  • filtering values.

3. Fast and Slow Pointers

One pointer moves faster than the other.

slow -> moves 1 step
fast -> moves 2 steps
Enter fullscreen mode Exit fullscreen mode

This is useful for:

  • detecting cycles;
  • finding the middle of a linked list;
  • removing the nth node from the end.

Step-by-Step Method

Use this process when solving a Two Pointers problem.

Step 1: Understand the Input

Ask:

What is the sequence?
Enter fullscreen mode Exit fullscreen mode

Example:

numbers = [2, 7, 11, 15]
Enter fullscreen mode Exit fullscreen mode

The sequence is a list of numbers.

Step 2: Decide Where the Pointers Start

For a sorted array pair problem:

left = 0
right = length - 1
Enter fullscreen mode Exit fullscreen mode

Example:

[2, 7, 11, 15]
 L          R
Enter fullscreen mode Exit fullscreen mode

Step 3: Write the Movement Rules

Before coding, write the rules in plain English.

For Two Sum II:

If sum == target, return the answer.
If sum < target, move the left pointer to the right.
If sum > target, move the right pointer to the left.
Enter fullscreen mode Exit fullscreen mode

Step 4: Trace the Example by Hand

Example:

numbers = [2, 7, 11, 15]
target = 9
Enter fullscreen mode Exit fullscreen mode

Trace:

left = 0, right = 3
2 + 15 = 17
17 is greater than 9
Move right left

left = 0, right = 2
2 + 11 = 13
13 is greater than 9
Move right left

left = 0, right = 1
2 + 7 = 9
Found the answer
Enter fullscreen mode Exit fullscreen mode

Answer:

[1, 2]
Enter fullscreen mode Exit fullscreen mode

Step 5: Write the Code

Example in Elixir:

defmodule Solution do
  @spec two_sum([integer()], integer()) :: [integer()]
  def two_sum(numbers, target) do
    values = List.to_tuple(numbers)
    search(values, target, 0, tuple_size(values) - 1)
  end

  defp search(values, target, left, right) do
    sum = elem(values, left) + elem(values, right)

    cond do
      sum == target ->
        [left + 1, right + 1]

      sum < target ->
        search(values, target, left + 1, right)

      sum > target ->
        search(values, target, left, right - 1)
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Example: Valid Palindrome

Problem:

Given a string, return true if it reads the same forward and backward.

Example:

"level"
Enter fullscreen mode Exit fullscreen mode

Expected output:

true
Enter fullscreen mode Exit fullscreen mode

Pointer idea:

l e v e l
L       R
Enter fullscreen mode Exit fullscreen mode

Rules:

If characters are different, return false.
If characters are equal, move both pointers inward.
If pointers meet or cross, return true.
Enter fullscreen mode Exit fullscreen mode

Elixir solution:

defmodule Solution do
  @spec palindrome?(String.t()) :: boolean()
  def palindrome?(s) do
    chars = s |> String.graphemes() |> List.to_tuple()
    check(chars, 0, tuple_size(chars) - 1)
  end

  defp check(_chars, left, right) when left >= right, do: true

  defp check(chars, left, right) do
    if elem(chars, left) == elem(chars, right) do
      check(chars, left + 1, right - 1)
    else
      false
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Trace:

"level"

left = 0, right = 4
l == l

left = 1, right = 3
e == e

left = 2, right = 2
stop

true
Enter fullscreen mode Exit fullscreen mode

How to Practice Two Pointers

Follow this routine:

  1. Read the problem.
  2. Identify the sequence.
  3. Choose where each pointer starts.
  4. Write the movement rules in English.
  5. Trace one example manually.
  6. Write the code.
  7. Test with small inputs.
  8. Explain why each pointer movement is safe.

Do not jump straight to code.

The pointer movement is the most important part.

Exercises

Exercise 1: Valid Palindrome

Input:

"racecar"
Enter fullscreen mode Exit fullscreen mode

Expected output:

true
Enter fullscreen mode Exit fullscreen mode

Practice:

  • Start one pointer at the beginning.
  • Start one pointer at the end.
  • Compare both characters.
  • Move inward when they match.

Extra tests:

"level" -> true
"hello" -> false
"a" -> true
Enter fullscreen mode Exit fullscreen mode

Exercise 2: Reverse List

Input:

[1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Expected output:

[5, 4, 3, 2, 1]
Enter fullscreen mode Exit fullscreen mode

Practice:

  • Start one pointer at the beginning.
  • Start one pointer at the end.
  • Swap both values.
  • Move both pointers inward.

Extra tests:

[] -> []
[1] -> [1]
[1, 2] -> [2, 1]
Enter fullscreen mode Exit fullscreen mode

Exercise 3: Two Sum II

Input:

numbers = [2, 7, 11, 15]
target = 9
Enter fullscreen mode Exit fullscreen mode

Expected output:

[1, 2]
Enter fullscreen mode Exit fullscreen mode

Practice:

  • Use a sorted array.
  • Move the left pointer when the sum is too small.
  • Move the right pointer when the sum is too large.

Extra tests:

[1, 2, 3, 4, 6], target = 6 -> [2, 4]
[2, 3, 4], target = 6 -> [1, 3]
[-1, 0], target = -1 -> [1, 2]
Enter fullscreen mode Exit fullscreen mode

Exercise 4: Move Zeroes

Input:

[0, 1, 0, 3, 12]
Enter fullscreen mode Exit fullscreen mode

Expected output:

[1, 3, 12, 0, 0]
Enter fullscreen mode Exit fullscreen mode

Practice:

  • Use one pointer to read.
  • Use another pointer to track where the next non-zero value should go.
  • Keep the original order of non-zero values.

Extra tests:

[0] -> [0]
[1, 0] -> [1, 0]
[0, 0, 1] -> [1, 0, 0]
Enter fullscreen mode Exit fullscreen mode

Exercise 5: Container With Most Water

Input:

[1, 8, 6, 2, 5, 4, 8, 3, 7]
Enter fullscreen mode Exit fullscreen mode

Expected output:

49
Enter fullscreen mode Exit fullscreen mode

Practice:

  • Start with the widest container.
  • Calculate the current area.
  • Move the pointer with the smaller height.
  • Keep the best area found.

Key idea:

The shorter side limits the area.
Move the shorter side because it is the only side that can improve the result.
Enter fullscreen mode Exit fullscreen mode

Final Checklist

Before using Two Pointers, answer:

  1. Where does the left pointer start?
  2. Where does the right pointer start?
  3. What does each pointer represent?
  4. What condition moves the left pointer?
  5. What condition moves the right pointer?
  6. When does the algorithm stop?
  7. Why is each pointer movement safe?
  8. What is the time complexity?
  9. What is the space complexity?

If you can answer these questions, you understand the pattern.


This article was originally published on AlchemistDrops.

Top comments (0)