← Back to home

Two Pointers

By Pscodium · 8/4/2026 · 2 views

Two Pointers — Uma Técnica para Resolver Problemas com Arrays

Two Pointers (dois ponteiros) é uma das técnicas mais importantes para resolver problemas envolvendo arrays e strings.

A ideia é simples:

Utilizar duas variáveis para acompanhar diferentes posições dentro de uma estrutura de dados.

Em vez de comparar todos os elementos entre si, podemos utilizar dois ponteiros para percorrer os dados de forma inteligente.

Essa técnica aparece frequentemente em problemas do LeetCode e é muito utilizada para resolver problemas com complexidade:

O(n)

em vez de:

O(n²)

1. O conceito básico

Imagine o seguinte array:

[1, 2, 3, 4, 5]

Podemos ter dois ponteiros:

left
  ↓
[1, 2, 3, 4, 5]
             ↑
            right

Inicialmente:

left = 0
right = 4

Ou seja:

left  → primeiro elemento

right → último elemento

Podemos movimentar os ponteiros:

left++

ou:

right--

Dependendo do problema.


2. O exemplo clássico: Two Sum II

Um problema muito conhecido do LeetCode é o:

Two Sum II — Input Array Is Sorted

Temos um array ordenado:

numbers = [2, 7, 11, 15]

target = 9

Precisamos encontrar dois números cuja soma seja 9.

Sabemos que:

2 + 7 = 9

Então o resultado é:

[1, 2]

Nesse problema, os índices começam em 1.


3. A solução com Two Pointers

Como o array está ordenado:

[2, 7, 11, 15]
 ↑           ↑
left       right

Começamos com:

left = 0
right = 3

Pegamos:

2 + 15 = 17

Como:

17 > 9

Precisamos diminuir a soma.

Como o array está ordenado, movemos:

right--

Agora:

[2, 7, 11, 15]
 ↑       ↑
left   right

Temos:

2 + 11 = 13

Ainda é maior:

13 > 9

Movemos novamente:

right--

Agora:

[2, 7, 11, 15]
 ↑   ↑
left right

Temos:

2 + 7 = 9

Encontramos a resposta.


4. Implementação em TypeScript

function twoSum(
  numbers: number[],
  target: number
): number[] {
  let left = 0;
  let right = numbers.length - 1;

  while (left < right) {
    const sum = numbers[left] + numbers[right];

    if (sum === target) {
      return [left + 1, right + 1];
    }

    if (sum > target) {
      right--;
    } else {
      left++;
    }
  }

  return [];
}

Uso:

const result = twoSum(
  [2, 7, 11, 15],
  9
);

console.log(result);

Resultado:

[1, 2]

5. Por que funciona?

A chave está no fato de que o array está ordenado.

Temos:

[2, 7, 11, 15]
 ↑           ↑
left       right

Se:

2 + 15 > target

sabemos que precisamos diminuir a soma.

Como o array está ordenado, podemos mover:

right--

Se:

2 + 7 < target

precisamos aumentar a soma.

Movemos:

left++

A lógica é:

Soma muito grande
    ↓
right--

Soma muito pequena
    ↓
left++

Soma correta
    ↓
Encontramos

6. Complexidade

A solução percorre o array apenas uma vez.

Cada ponteiro pode avançar, mas nunca volta.

Portanto:

Time Complexity: O(n)

E não precisamos de uma estrutura auxiliar:

Space Complexity: O(1)

Isso é melhor do que a solução de força bruta:

O(n²)

e também não utiliza a memória adicional:

O(n)

da solução com HashMap.


7. Two Pointers em strings

A técnica também funciona muito bem com strings.

Um exemplo clássico é verificar se uma string é um palíndromo.

Um palíndromo é uma palavra que permanece igual quando lida de trás para frente.

Por exemplo:

radar

Temos:

r a d a r
↑       ↑
L       R

Comparamos:

r === r

Movemos:

  ↑   ↑
  L   R

Comparamos:

a === a

Movemos novamente:

    ↑
    d

Chegamos ao centro.

É um palíndromo.


8. Implementando um Palíndromo

function isPalindrome(s: string): boolean {
  let left = 0;
  let right = s.length - 1;

  while (left < right) {
    if (s[left] !== s[right]) {
      return false;
    }

    left++;
    right--;
  }

  return true;
}

Podemos testar:

console.log(isPalindrome("radar"));

Resultado:

true

Outro exemplo:

console.log(isPalindrome("hello"));

Resultado:

false

9. Visualizando o algoritmo

Para:

radar

Começamos:

r a d a r
↑       ↑
L       R

Comparamos:

r === r

Movemos:

  ↑   ↑
  L   R

Comparamos:

a === a

Movemos:

    ↑
    L/R

Chegamos ao centro.

Resultado:

true

10. Outro exemplo: Reverse String

Também podemos utilizar Two Pointers para inverter uma string ou array.

Temos:

["h", "e", "l", "l", "o"]

Começamos:

["h", "e", "l", "l", "o"]
  ↑                 ↑
 left              right

Trocamos:

h ↔ o

Resultado:

["o", "e", "l", "l", "h"]

Movemos os ponteiros:

["o", "e", "l", "l", "h"]
      ↑       ↑
     left   right

Trocamos:

e ↔ l

Resultado:

["o", "l", "l", "e", "h"]

Agora terminamos.


11. Implementação

function reverseString(chars: string[]): void {
  let left = 0;
  let right = chars.length - 1;

  while (left < right) {
    [chars[left], chars[right]] = [
      chars[right],
      chars[left]
    ];

    left++;
    right--;
  }
}

Uso:

const chars = ["h", "e", "l", "l", "o"];

reverseString(chars);

console.log(chars);

Resultado:

["o", "l", "l", "e", "h"]

A vantagem é que fazemos a operação in-place, sem precisar criar outro array.

Complexidade:

Time: O(n)

Space: O(1)

12. Two Pointers não precisa ser Left e Right

É comum pensar que Two Pointers sempre significa:

left →

← right

Mas não necessariamente.

Podemos ter dois ponteiros que percorrem a estrutura na mesma direção.

Por exemplo:

slow
  ↓
[1, 2, 3, 4, 5]
  ↑
fast

O slow avança mais devagar.

O fast avança mais rapidamente.

Esse padrão é conhecido como:

Slow and Fast Pointers


13. Exemplo: remover duplicatas

Imagine um array ordenado:

[1, 1, 2, 2, 3]

Queremos remover as duplicatas.

Podemos usar dois ponteiros:

slow
 ↓
[1, 1, 2, 2, 3]
    ↑
   fast

O fast percorre todos os elementos.

O slow representa a posição onde devemos colocar o próximo valor único.

Implementação:

function removeDuplicates(
  numbers: number[]
): number {
  if (numbers.length === 0) {
    return 0;
  }

  let slow = 0;

  for (
    let fast = 1;
    fast < numbers.length;
    fast++
  ) {
    if (numbers[fast] !== numbers[slow]) {
      slow++;

      numbers[slow] = numbers[fast];
    }
  }

  return slow + 1;
}

Depois:

[1, 2, 3, 2, 3]

Os primeiros elementos representam:

[1, 2, 3]

O tamanho válido é:

3

14. Two Pointers e Arrays Ordenados

Um dos principais sinais de que podemos utilizar Two Pointers é quando o problema envolve:

Um array ordenado.

Por exemplo:

[1, 2, 3, 5, 7, 9]

Podemos começar:

left → 1

right → 9

E usar a ordenação para decidir qual ponteiro mover.

Se a soma for muito alta:

right--

Se for muito baixa:

left++

Essa propriedade permite eliminar rapidamente grandes partes do espaço de busca.


15. Exemplo prático: encontrar dois preços

Imagine um e-commerce com produtos ordenados por preço:

[10, 20, 30, 40, 50, 60]

Queremos encontrar dois produtos cujo preço combinado seja:

70

Começamos:

10 + 60 = 70

Encontramos imediatamente.

Agora imagine:

target = 80

Começamos:

10 + 60 = 70

É menor que 80.

Movemos:

left++

Agora:

20 + 60 = 80

Encontramos.

O algoritmo poderia ser:

function findProducts(
  prices: number[],
  target: number
): [number, number] | null {
  let left = 0;
  let right = prices.length - 1;

  while (left < right) {
    const total = prices[left] + prices[right];

    if (total === target) {
      return [
        prices[left],
        prices[right]
      ];
    }

    if (total < target) {
      left++;
    } else {
      right--;
    }
  }

  return null;
}

Uso:

const result = findProducts(
  [10, 20, 30, 40, 50, 60],
  80
);

console.log(result);

Resultado:

[20, 60]

16. Quando pensar em Two Pointers?

Existem alguns sinais comuns.

1. O array está ordenado

[1, 2, 3, 4, 5]

Pode ser possível usar:

left
right

2. Você precisa encontrar um par

Por exemplo:

a + b = target

ou:

a + b < target

ou:

a + b > target

3. Você precisa comparar extremos

Por exemplo:

Primeiro ↔ Último

Isso aparece em:

  • Palíndromos
  • Strings
  • Arrays
  • Reverse
  • Comparações

4. Você precisa percorrer duas partes simultaneamente

Por exemplo:

slow
fast

Esse padrão aparece em:

  • Remoção de duplicatas
  • Linked Lists
  • Detecção de ciclos
  • Encontrar o meio de uma lista

17. Two Pointers vs HashMap

No problema Two Sum, podemos ter diferentes soluções.

HashMap

Funciona mesmo quando o array não está ordenado:

[3, 2, 4]

Complexidade:

O(n)

Memória:

O(n)

Two Pointers

Normalmente exige que os dados estejam ordenados:

[2, 3, 4]

Complexidade:

O(n)

Memória:

O(1)

Mas existe um detalhe.

Se o array original não estiver ordenado, precisamos ordená-lo.

Isso pode custar:

O(n log n)

Portanto, a escolha depende do problema.


18. A grande ideia

O principal benefício do Two Pointers é evitar comparações desnecessárias.

Uma abordagem de força bruta poderia fazer:

Elemento 1
    ↓
Comparar com todos

Elemento 2
    ↓
Comparar com todos

Elemento 3
    ↓
Comparar com todos

Complexidade:

O(n²)

Com Two Pointers:

left →
       ← right

Os ponteiros se aproximam.

Cada elemento é visitado no máximo algumas vezes.

Resultado:

O(n)

Conclusão

Two Pointers é uma técnica utilizada para resolver problemas percorrendo uma estrutura de dados com dois índices ou referências.

O padrão mais conhecido é:

left →            ← right

E funciona especialmente bem quando:

  • O array está ordenado.
  • Precisamos encontrar pares.
  • Precisamos comparar extremos.
  • Podemos decidir qual ponteiro mover com base no resultado atual.

Os exemplos mais comuns incluem:

Two Sum II
Valid Palindrome
Reverse String
Remove Duplicates

A ideia pode ser resumida em:

                    Problema
                       │
                       ▼
             Usar dois ponteiros
                       │
            ┌──────────┴──────────┐
            ▼                     ▼
        left/right            slow/fast
            │                     │
            ▼                     ▼
      Extremos do array       Velocidades

A principal lição é reconhecer quando o problema permite eliminar partes da busca sem precisar testá-las individualmente.

Se você consegue determinar, com base no estado atual, qual dos dois ponteiros deve avançar, existe uma boa chance de que Two Pointers seja a solução.



Comments

No comments yet.