---
title: "Two Pointers (Dois Ponteiros) em Zig — Implementação e Explicação"
url: "https://ziglang.com.br/algoritmos/two-pointers-dois-ponteiros-em-zig-implementa%C3%A7%C3%A3o-e-explica%C3%A7%C3%A3o/"
markdown_url: "https://ziglang.com.br/algoritmos/two-pointers-dois-ponteiros-em-zig-implementa%C3%A7%C3%A3o-e-explica%C3%A7%C3%A3o.MD"
description: "Aprenda a técnica Two Pointers implementada em Zig. Resolva problemas de arrays ordenados em O(n) com código funcional."
date: "2026-02-21"
author: "Zig Brasil"
---

# Two Pointers (Dois Ponteiros) em Zig — Implementação e Explicação

Aprenda a técnica Two Pointers implementada em Zig. Resolva problemas de arrays ordenados em O(n) com código funcional.


# Two Pointers (Dois Ponteiros) em Zig — Implementação e Explicação

A técnica **Two Pointers** (dois ponteiros) usa dois índices que percorrem um array de forma coordenada, geralmente de extremidades opostas ou na mesma direção. Permite resolver em O(n) problemas que ingenuamente seriam O(n^2).

## Como Funciona

### Variantes Principais

1. **Ponteiros opostos**: um no início, outro no fim, movem-se para o centro
2. **Ponteiros na mesma direção**: ambos começam no início (fast/slow)
3. **Ponteiros em duas sequências**: um em cada sequência

### Visualização

```
Problema: encontrar par que soma 15 em array ordenado

Array: [1, 3, 5, 7, 8, 10, 12]
        L                   R    soma=13 < 15 → L++
           L                R    soma=15 = 15 → encontrado! (3, 12)

Problema: remover duplicatas in-place

Array: [1, 1, 2, 2, 3, 4, 4]
        S  F                     1==1, F++
        S     F                  1!=2, S++, arr[S]=2
           S     F               2==2, F++
           S        F            2!=3, S++, arr[S]=3
              S        F         3!=4, S++, arr[S]=4
              S           F      3!=4... já copiou
Resultado: [1, 2, 3, 4, ...]  (4 elementos únicos)
```

## Implementação em Zig

```zig
const std = @import("std");
const Allocator = std.mem.Allocator;

/// Encontra um par de elementos que soma o valor alvo.
/// Array deve estar ordenado. Retorna os índices ou null.
pub fn parComSoma(arr: []const i32, alvo: i32) ?[2]usize {
    if (arr.len < 2) return null;

    var esq: usize = 0;
    var dir: usize = arr.len - 1;

    while (esq < dir) {
        const soma = arr[esq] + arr[dir];
        if (soma == alvo) {
            return .{ esq, dir };
        } else if (soma < alvo) {
            esq += 1;
        } else {
            dir -= 1;
        }
    }
    return null;
}

/// Remove duplicatas in-place de um array ordenado.
/// Retorna o número de elementos únicos.
pub fn removerDuplicatas(arr: []i32) usize {
    if (arr.len <= 1) return arr.len;

    var lento: usize = 0;
    for (1..arr.len) |rapido| {
        if (arr[rapido] != arr[lento]) {
            lento += 1;
            arr[lento] = arr[rapido];
        }
    }
    return lento + 1;
}

/// Verifica se uma string é palíndromo.
pub fn ehPalindromo(s: []const u8) bool {
    if (s.len <= 1) return true;

    var esq: usize = 0;
    var dir: usize = s.len - 1;
    while (esq < dir) {
        if (s[esq] != s[dir]) return false;
        esq += 1;
        dir -= 1;
    }
    return true;
}

/// Encontra o container com mais água (LeetCode 11).
/// Cada elemento é a altura de uma barra.
pub fn containerComMaisAgua(alturas: []const u32) u64 {
    if (alturas.len < 2) return 0;

    var esq: usize = 0;
    var dir: usize = alturas.len - 1;
    var max_agua: u64 = 0;

    while (esq < dir) {
        const largura: u64 = @intCast(dir - esq);
        const altura = @min(alturas[esq], alturas[dir]);
        max_agua = @max(max_agua, largura * altura);

        if (alturas[esq] < alturas[dir]) {
            esq += 1;
        } else {
            dir -= 1;
        }
    }
    return max_agua;
}

/// Merge de dois arrays ordenados.
pub fn mergeDoisArrays(
    allocator: Allocator,
    a: []const i32,
    b: []const i32,
) ![]i32 {
    var resultado = try allocator.alloc(i32, a.len + b.len);
    var i: usize = 0;
    var j: usize = 0;
    var k: usize = 0;

    while (i < a.len and j < b.len) {
        if (a[i] <= b[j]) {
            resultado[k] = a[i];
            i += 1;
        } else {
            resultado[k] = b[j];
            j += 1;
        }
        k += 1;
    }
    while (i < a.len) {
        resultado[k] = a[i];
        i += 1;
        k += 1;
    }
    while (j < b.len) {
        resultado[k] = b[j];
        j += 1;
        k += 1;
    }
    return resultado;
}

/// Three Sum: encontra todos os tripletos que somam zero.
pub fn threeSum(allocator: Allocator, arr: []i32) ![][3]i32 {
    var resultados = std.ArrayList([3]i32).init(allocator);

    // Ordena primeiro
    std.mem.sort(i32, arr, {}, std.sort.asc(i32));

    for (0..arr.len) |i| {
        if (i > 0 and arr[i] == arr[i - 1]) continue; // pula duplicatas

        var esq = i + 1;
        var dir = arr.len - 1;
        while (esq < dir) {
            const soma = @as(i64, arr[i]) + arr[esq] + arr[dir];
            if (soma == 0) {
                try resultados.append(.{ arr[i], arr[esq], arr[dir] });
                while (esq < dir and arr[esq] == arr[esq + 1]) esq += 1;
                while (esq < dir and arr[dir] == arr[dir - 1]) dir -= 1;
                esq += 1;
                dir -= 1;
            } else if (soma < 0) {
                esq += 1;
            } else {
                dir -= 1;
            }
        }
    }
    return try resultados.toOwnedSlice();
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Par com soma
    const arr = [_]i32{ 1, 3, 5, 7, 8, 10, 12 };
    if (parComSoma(&arr, 15)) |par| {
        try stdout.print("Par com soma 15: arr[{d}]={d}, arr[{d}]={d}\n", .{
            par[0], arr[par[0]], par[1], arr[par[1]],
        });
    }

    // Remover duplicatas
    var dup = [_]i32{ 1, 1, 2, 2, 3, 4, 4 };
    const unicos = removerDuplicatas(&dup);
    try stdout.print("Sem duplicatas ({d}): ", .{unicos});
    for (dup[0..unicos]) |x| try stdout.print("{d} ", .{x});
    try stdout.print("\n", .{});

    // Palíndromo
    try stdout.print("\"arara\" é palíndromo? {}\n", .{ehPalindromo("arara")});
    try stdout.print("\"zig\" é palíndromo? {}\n", .{ehPalindromo("zig")});

    // Container com mais água
    const alturas = [_]u32{ 1, 8, 6, 2, 5, 4, 8, 3, 7 };
    try stdout.print("Max água: {d}\n", .{containerComMaisAgua(&alturas)});

    // Merge
    const a = [_]i32{ 1, 3, 5, 7 };
    const b = [_]i32{ 2, 4, 6, 8 };
    const merged = try mergeDoisArrays(allocator, &a, &b);
    defer allocator.free(merged);
    try stdout.print("Merge: ", .{});
    for (merged) |x| try stdout.print("{d} ", .{x});
    try stdout.print("\n", .{});
}
```

## Análise de Complexidade

| Problema | Tempo | Espaço |
|----------|-------|--------|
| **Par com soma** | O(n) | O(1) |
| **Remover duplicatas** | O(n) | O(1) |
| **Palíndromo** | O(n) | O(1) |
| **Container** | O(n) | O(1) |
| **Three Sum** | O(n^2) | O(1) |

## Quando Usar

- Array/lista **ordenada** (ou pode ser ordenada)
- Problemas envolvendo **pares** ou **subconjuntos**
- Quando a solução ingênua usa dois loops aninhados

## Recursos Relacionados

- [Sliding Window](/algoritmos/sliding-window/) — Técnica complementar para janelas
- [Busca Binária](/algoritmos/busca-binaria/) — Outra técnica para arrays ordenados
- [Merge Sort](/algoritmos/merge-sort/) — Usa merge de dois arrays
- [Array Dinâmico](/estruturas-dados/array-dinamico/) — Estrutura base
