Lógica de ProgramaçãoFundamental· 9 min de leitura

Complexidade de Algoritmos: medir a eficiência do código

Dois programas podem resolver o mesmo problema, mas um leva milissegundos e o outro horas. A complexidade de algoritmos explica por quê — e como escolher o mais eficiente.

RF

Renato Freitas

Atualizado em 27 de abril de 2026

Por que a eficiência dos algoritmos importa?

Um algoritmo que funciona perfeitamente com 100 elementos pode se tornar inutilizável com 10 milhões. A diferença entre um algoritmo ruim e um bom não é de segundos — pode ser de dias. Entender complexidade é o que separa código que escala de código que trava.

Exemplo real: buscar um nome em uma lista telefônica de 1 milhão de entradas. Se você verificar um a um desde o início, pode precisar de 1 milhão de comparações. Com a busca binária (que funciona em listas ordenadas), você encontra qualquer nome em no máximo 20 comparações. A diferença é de 1.000.000 para 20.

Complexidade de algoritmos é a área da ciência da computação que estuda como o tempo de execução e o uso de memória crescem conforme o tamanho da entrada aumenta. A ferramenta principal é a notação Big-O.

🧮 Teste você mesmo — CalcSim

Quer mais recursos? Baixar app CalcSim IA

Notação Big-O: descrevendo o crescimento

A notação Big-O descreve como o custo de um algoritmo cresce em relação ao tamanho da entrada n, no pior caso. Ela ignora constantes e termos menores para focar no comportamento dominante com entradas grandes.

O(1) — tempo constante: o algoritmo leva o mesmo tempo independentemente do tamanho da entrada. Acessar um elemento de um array pelo índice é O(1) — não importa se o array tem 10 ou 10 milhões de elementos, o acesso é igualmente rápido.

O(n) — tempo linear: o tempo cresce proporcionalmente ao tamanho da entrada. Percorrer todos os elementos de uma lista para encontrar o maior valor é O(n): com o dobro de elementos, leva o dobro do tempo.

O(n²) — tempo quadrático: o tempo cresce com o quadrado do tamanho da entrada. Algoritmos com dois loops aninhados que percorrem toda a coleção costumam ser O(n²). Com 1000 elementos são necessárias até 1.000.000 operações.

O(log n) — tempo logarítmico: o tempo cresce muito lentamente. A cada passo, o problema é dividido ao meio. A busca binária em uma lista ordenada de 1 bilhão de elementos precisa de no máximo 30 comparações.

  • O(1): constante — acessar array por índice, inserir no início de uma lista ligada.
  • O(log n): logarítmico — busca binária, operações em árvore balanceada.
  • O(n): linear — percorrer uma lista, busca linear.
  • O(n log n): linearítmico — merge sort, heap sort. Eficiente para ordenação.
  • O(n²): quadrático — bubble sort, insertion sort em listas grandes.
  • O(2^n): exponencial — força bruta em subconjuntos. Inviável para n > 30.

Busca linear vs busca binária

A busca linear percorre os elementos um a um desde o início até encontrar o alvo ou chegar ao final. É simples, funciona em qualquer lista (ordenada ou não), mas é O(n) — no pior caso, verifica todos os elementos.

A busca binária funciona apenas em listas ordenadas. Ela começa pelo elemento do meio: se o alvo é menor, descarta a metade superior e repete na metade inferior; se é maior, descarta a metade inferior. A cada passo, o espaço de busca é dividido ao meio, resultando em complexidade O(log n).

Para uma lista de 1.000.000 de elementos, a busca linear pode precisar de até 1.000.000 comparações. A busca binária precisa de no máximo 20. O custo de manter a lista ordenada é amortizado quando há muitas buscas — ordenar uma vez e buscar rapidamente muitas vezes vale a pena.

  • Busca linear: O(n) — funciona em listas não ordenadas. Simples de implementar.
  • Busca binária: O(log n) — exige lista ordenada. Muito mais rápida para listas grandes.
  • Ponto de equilíbrio: para listas muito pequenas (menos de 10 elementos), a diferença é irrelevante.

Algoritmos de ordenação: do Bubble Sort ao Merge Sort

Ordenar uma coleção é uma das tarefas mais comuns em programação. A escolha do algoritmo de ordenação impacta diretamente a performance para coleções grandes.

O Bubble Sort é o mais simples de entender: percorre a lista repetidamente, comparando pares adjacentes e trocando os que estão fora de ordem. O maior elemento vai borbulhando para o final a cada passagem. É intuitivo, mas tem complexidade O(n²) — extremamente lento para listas grandes. Não é usado em produção.

O Merge Sort (ordenação por mesclagem) usa a estratégia de divisão e conquista: divide a lista ao meio recursivamente até ter listas de um elemento (que já estão ordenadas), depois mescla os pares de listas menores em ordem crescente. A mesclagem de duas listas ordenadas é O(n), e o processo se repete O(log n) vezes, resultando em O(n log n) no total. Essa é a complexidade ótima para algoritmos de ordenação baseados em comparação.

As linguagens modernas usam variantes do Merge Sort ou Quick Sort internamente. Python usa Timsort (híbrido de Merge Sort e Insertion Sort). Java e JavaScript usam variantes similares. Você raramente precisará implementar um algoritmo de ordenação do zero — mas entender a complexidade ajuda a tomar decisões informadas.

  • Bubble Sort: O(n²) — educacional, nunca use em produção com dados reais.
  • Insertion Sort: O(n²) no pior caso, mas O(n) para listas quase ordenadas. Útil para listas pequenas.
  • Merge Sort: O(n log n) garantido. Estável (preserva a ordem original de elementos iguais).
  • Quick Sort: O(n log n) na média, O(n²) no pior caso. Muito rápido na prática.
  • Heap Sort: O(n log n) garantido. Usado quando memória extra é restrição.

Complexidade de espaço e como pensar em trade-offs

Além do tempo, algoritmos também consomem memória. A complexidade de espaço mede como o uso de memória cresce com o tamanho da entrada. Um algoritmo pode ser rápido (O(n log n)) mas consumir O(n) de memória extra — o que pode ser um problema em dispositivos com memória limitada.

Trade-off é a troca consciente entre recursos. Às vezes, usar mais memória permite reduzir o tempo de execução. Um exemplo clássico é a memoização: armazenar resultados já calculados para não recalcular. O cálculo recursivo de Fibonacci sem memoização é O(2^n); com memoização, cai para O(n) no tempo ao custo de O(n) em memória.

Na prática, o primeiro passo é fazer o algoritmo funcionar corretamente. Depois, se houver problema de performance, meça antes de otimizar — ferramentas de profiling mostram exatamente onde o tempo está sendo gasto. Otimizar o código certo é muito mais eficaz do que otimizar tudo às cegas.

  • Complexidade de tempo: como o tempo cresce com n.
  • Complexidade de espaço: como o uso de memória cresce com n.
  • Trade-off tempo × espaço: mais memória pode significar menos tempo, e vice-versa.
  • Medir antes de otimizar: use profiling para encontrar o verdadeiro gargalo.

Perguntas frequentes

Preciso memorizar todas as notações Big-O?

Não é necessário memorizar tabelas. O mais importante é desenvolver a intuição: um loop simples sobre n elementos é O(n); dois loops aninhados sobre n elementos são O(n²); dividir o problema ao meio a cada passo é O(log n). Com essa base, você consegue estimar a complexidade de qualquer algoritmo que escrever.

Por que o Big-O ignora constantes? O(2n) e O(n) são iguais?

Sim, na notação Big-O, O(2n) é simplificado para O(n). O objetivo do Big-O é descrever o comportamento assintótico — como o algoritmo escala para entradas muito grandes. Uma constante multiplicativa não altera o crescimento fundamental: se dobrar o tamanho da entrada dobrar o tempo, o algoritmo é linear, seja O(n) ou O(5n). O que importa é a classe de crescimento, não o coeficiente exato.

Quando um algoritmo O(n²) pode ser melhor que um O(n log n)?

Para entradas muito pequenas, sim. Algoritmos O(n²) simples como Insertion Sort têm overhead muito baixo e podem ser mais rápidos que Merge Sort para listas de menos de 10 a 20 elementos. É por isso que implementações reais de Merge Sort e Quick Sort geralmente usam Insertion Sort para sublistas pequenas. O Big-O descreve o comportamento para n grande — para n pequeno, constantes e overhead importam.

Este artigo foi útil para você?

Avalie com estrelas para nos ajudar a melhorar o conteúdo.

Faça login para avaliar este artigo.

Ainda tem dúvida?

O Professor IA explica passo a passo

Faça uma pergunta em linguagem natural e receba uma explicação personalizada sobre Lógica de Programação — ou qualquer outro tópico.

Prefere resolver pelo celular?

Baixar o app grátis →

Continue aprendendo