DEV Community

Priscila Oliveira
Priscila Oliveira

Posted on

Big O Notation: Explicação Simples para Quem Não É Expert (Ainda!)

Se você já ouviu falar de Big O Notation, mas sente que isso é um bicho de sete cabeças, este artigo é para você. Vamos desmistificar esse conceito de forma simples, prática e sem matemática complicada. No final, você vai entender por que ele é importante e como usá-lo no dia a dia como desenvolvedor.

O que é Big O Notation?

Big O é uma ferramenta usada para medir a eficiência de algoritmos. Ele nos ajuda a entender como o tempo de execução ou o uso de memória de um algoritmo cresce à medida que a entrada aumenta.

Pense assim: se um programa roda rápido para 10 dados, ele continuará rápido para 1 milhão? O Big O nos dá uma forma de responder essa pergunta.

Por que o Big O é importante?

Imagine que você está construindo um aplicativo que precisa buscar informações em uma lista enorme. Se seu código não for eficiente, pode funcionar bem em testes pequenos, mas travar quando for para produção.

O Big O ajuda a prever problemas antes que eles aconteçam, tornando seu código mais escalável e performático.

Principais Tipos de Big O (Com Exemplos Simples em JavaScript)

Vamos analisar cada caso com o que fazer e o que evitar, usando exemplos em JavaScript.

O(1) - Tempo Constante

O que fazer (código eficiente)

function acessarPrimeiroItem(array) {
    return array[0]; // Sempre pega o primeiro item, independente do tamanho do array
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é bom?

  • A operação sempre ocorre no mesmo tempo, não importa se o array tem 10 ou 1 milhão de elementos.
  • O acesso direto a índices específicos é super rápido.

O que evitar (código ineficiente)

function acessarPrimeiroItemErrado(array) {
    for (let i = 0; i < array.length; i++) {
        if (i === 0) {
            return array[i];
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é ruim?

  • Criamos um loop desnecessário, tornando a função O(n) quando poderia ser O(1).
  • Com grandes entradas, esse código desperdiça processamento à toa.

💡 Dica: Sempre que possível, prefira acessos diretos em vez de loops desnecessários.

O(n) - Tempo Linear

O que fazer (código eficiente)

function imprimirElementos(array) {
    for (let item of array) {
        console.log(item);
    }
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é bom?

  • O código percorre o array uma única vez, garantindo eficiência.
  • Se houver 100 ou 1.000 elementos, a função cresce proporcionalmente.

O que evitar (código ineficiente)

function imprimirElementosErrado(array) {
    for (let i = 0; i < array.length; i++) {
        for (let j = 0; j < 1; j++) { // Loop extra sem necessidade
            console.log(array[i]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é ruim?

  • Um loop extra não faz sentido aqui e adiciona complexidade sem necessidade.
  • Mesmo que o loop interno rode apenas uma vez, ele pode confundir outras pessoas que leiam seu código.

💡 Dica: Evite loops desnecessários e mantenha a estrutura do código limpa.

O(n²) - Tempo Quadrático

O que fazer (código eficiente)

function encontrarPares(array) {
    for (let i = 0; i < array.length; i++) {
        for (let j = i + 1; j < array.length; j++) {
            console.log(array[i], array[j]); // Só imprime pares únicos
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é melhor?

  • O loop interno começa em i + 1, reduzindo pela metade o número de execuções.
  • Se há formas de reduzir loops aninhados, sempre vale a pena explorar.

O que evitar (código ineficiente)

function encontrarParesErrado(array) {
    for (let i = 0; i < array.length; i++) {
        for (let j = 0; j < array.length; j++) {
            console.log(array[i], array[j]); // Exibe pares repetidos
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é ruim?

  • O loop interno recomeça do zero a cada iteração, gerando muitas comparações desnecessárias.
  • Se o array tiver 1.000 elementos, serão 1.000.000 de operações!

💡 Dica: Sempre que precisar de loops aninhados, pergunte-se "posso reduzir isso?".

O(log n) - Tempo Logarítmico

O que fazer (código eficiente - busca binária)

function buscaBinaria(array, alvo) {
    let inicio = 0;
    let fim = array.length - 1;

    while (inicio <= fim) {
        let meio = Math.floor((inicio + fim) / 2);
        if (array[meio] === alvo) {
            return true;
        } else if (array[meio] < alvo) {
            inicio = meio + 1;
        } else {
            fim = meio - 1;
        }
    }
    return false;
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é ótimo?

  • A cada iteração, eliminamos metade dos elementos.
  • Um array com 1 milhão de elementos pode ser pesquisado com apenas 20 iterações!

O que evitar (código ineficiente - busca linear em vez de binária)

function buscaLinear(array, alvo) {
    for (let item of array) {
        if (item === alvo) {
            return true;
        }
    }
    return false;
}
Enter fullscreen mode Exit fullscreen mode

Por que isso é ruim?

  • Se o array for grande, pode precisar percorrer todos os elementos antes de encontrar a resposta.
  • Para dados ordenados, a busca binária é muito mais eficiente.

💡 Dica: Se o seu conjunto de dados estiver ordenado, sempre prefira a busca binária ao invés de uma busca linear.

Veja uma tabela que mostra de forma simples a eficiencia de cada um:

| Notação      | Exemplo Típico               | Escalabilidade               |
|--------------|------------------------------|------------------------------|
| **O(1)**     | Acessar um item em um array  | 🚀 Super rápido              | 
| **O(log n)** | Busca binária                | 🔥 Muito eficiente           |
| **O(n)**     | Percorrer um array           | ⚡ Razoável                   |
| **O(n²)**    | Loops aninhados              | 🐢 Lento em grandes entradas |
Enter fullscreen mode Exit fullscreen mode

Conclusão

Agora que você viu o que fazer e o que evitar, fica mais fácil aplicar o Big O Notation no seu código. Aqui estão os principais aprendizados:

  • Prefira O(1) sempre que possível (acessos diretos são mais rápidos).
  • Evite loops desnecessários e sempre questione se um O(n²) pode ser otimizado.
  • Se os dados forem ordenados, use busca binária (O(log n)) em vez de percorrer tudo (O(n)).
  • Código eficiente não é apenas rápido, mas também limpo e fácil de entender.

Ao escrever um algoritmo, pare e pense na eficiência dele. Seu código não precisa ser apenas funcional, mas também rápido e escalável!

Top comments (0)