Pular para o conteúdo

Algoritmos e estruturas de dados · Busca e ordenação · 5 min de leitura

Busca binária

Numa lista ordenada, olhe o meio e descarte metade: um milhão de itens em 20 comparações. E os detalhes que fazem quase toda implementação errar.

lo = 0, hi = 9 → meio [4] = 161meio [4] = 16 → meio [7] = 56: lo = 52meio [7] = 56 → meio [5] = 23: hi = 63lo = 0, hi = 9 — 10 itenslo = 0, hi = 910 itensmeio [4] = 16 — 23 > 16 → direitameio [4] = 1623 > 16 → direitameio [7] = 56 — 23 < 56 → esquerdameio [7] = 5623 < 56 → esquerdameio [5] = 23 — achoumeio [5] = 23achou

Arraste para o lado para ver o diagrama inteiro.

Cada comparação corta o intervalo pela metade: 10 → 5 → 2 → achou. Com um milhão de itens seriam no máximo 20 comparações.

A ideia

Só funciona com a lista ordenada. Compare o alvo com o elemento do meio: se for maior, ele só pode estar à direita; se for menor, à esquerda. Cada passo descarta metade, então o número de passos é log₂ n — O(log n).

Os erros clássicos

  • (lo + hi) / 2 pode estourar o Int com listas enormes — o bug que ficou anos no Arrays.binarySearch do Java. Use lo + (hi − lo) / 2.
  • Misturar intervalo fechado [lo, hi] com aberto [lo, hi): decida um e ajuste while (lo <= hi) e hi = meio − 1 de acordo.
  • Esquecer de mover lo ou hi para além do meio e entrar em laço infinito.

Muito além de achar um número

Busca binária serve para achar o primeiro item que satisfaz uma condição (o primeiro dia com estoque zerado, o commit que quebrou o build com git bisect) e até para "chutar" a resposta de um problema numérico. Na prática, use binarySearch do Kotlin ou partitioningIndex do Swift Algorithms.

No código: antes e depois

✗ AntesKotlin
fun busca(a: IntArray, alvo: Int): Int {
    var lo = 0; var hi = a.size - 1
    while (lo < hi) {               // perde o caso lo == hi
        val meio = (lo + hi) / 2    // pode estourar o Int
        if (a[meio] < alvo) lo = meio else hi = meio // pode travar
    }
    return if (a[lo] == alvo) lo else -1 // quebra com array vazio
}
✓ DepoisKotlin
fun busca(a: IntArray, alvo: Int): Int {
    var lo = 0
    var hi = a.size - 1             // intervalo fechado [lo, hi]
    while (lo <= hi) {
        val meio = lo + (hi - lo) / 2
        when {
            a[meio] == alvo -> return meio
            a[meio] < alvo -> lo = meio + 1
            else -> hi = meio - 1
        }
    }
    return -1 // ou use a.binarySearch(alvo)
}

Na versão ruim, com lo = 0, hi = 1 e alvo maior que a[0], meio fica 0 para sempre. A boa sempre encolhe o intervalo e cobre o array vazio.

Em resumo

Exercícios

Responda e confira na hora. Complexidades valem com ou sem espaços, como O(n log n).

  1. No máximo, quantas comparações a busca binária faz numa lista ordenada de 1.000.000 de itens?

    Ver a solução

    Resposta: 20. 2²⁰ = 1.048.576 ≥ 1.000.000, então 20 cortes pela metade bastam (log₂ 1.000.000 ≈ 19,9).

  2. Buscando 23 em [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] com meio = (lo + hi) / 2 arredondado para baixo, qual é o índice do primeiro meio?

    Ver a solução

    Resposta: 4. lo = 0, hi = 9: (0 + 9) / 2 = 4,5 → 4. O elemento é 16.

  3. Qual a condição que a lista precisa cumprir para a busca binária funcionar?

    Ver a solução

    Resposta: ordenada. Só numa lista ordenada dá para saber de que lado do meio o alvo está.

Perguntar à IA

Monte um prompt pronto sobre Busca binária na sua linguagem e no seu caso, e abra num chat gratuito.

ChatGPT Claude Perplexity

O prompt vai para o site que você escolher. Não cole senhas, tokens nem dados reais de clientes. Para Gemini e outros, copie e cole.

Referências

Continue aprendendo

Todos os temas de algoritmos e estruturas de dados →