Pular para o conteúdo

Fundamentos

Busca linear e busca binária em Python

Aprenda a procurar um valor numa lista com a busca linear e com a binária, conte os passos de cada uma e veja usos reais com a ESP32.

Por Prof. Rodrigo Faustino · Atualizado em · 18 min de leitura

Buscar é responder a uma pergunta que todo programa faz o tempo todo: este valor está na lista, e em que posição? Um cartão de acesso está entre os autorizados, um código de aluno existe na turma, a leitura de um sensor já passou do limite. Existem duas formas clássicas de responder, e a diferença entre elas é um bom primeiro contato com a ideia de algoritmo eficiente. A busca linear olha os elementos um a um e funciona em qualquer lista. A busca binária olha o elemento do meio e descarta metade da lista a cada passo, mas só funciona quando a lista está ordenada.

Este artigo escreve as duas à mão, conta quantas comparações cada uma faz, mostra o que acontece quando a binária recebe uma lista fora de ordem, e apresenta três usos na ESP32. Todos os exemplos foram executados no simulador, e os resultados citados são os que ele produz. Uma ressalva já no começo: o módulo bisect do Python de computador, que traz a busca binária pronta, não existe no simulador, e por isso escreveremos a busca nós mesmos, o que de qualquer modo é o exercício que interessa.

Busca linear: um por vez

Lista ordenada com os números 2, 5, 8, 12, 16, 23, 38, 56, 72 e 91 nas posições de 0 a 9. Os cinco primeiros estão em cinza com os números de 1 a 5, o 23 na posição 5 está em verde com o número 6, e os quatro últimos estão apagados porque nem foram olhados. Embaixo, dois quadros: buscar o 23 devolve 5 com 6 comparações, e buscar o 7 devolve menos 1 com 10 comparações.
A busca linear olha um elemento por vez, da esquerda para a direita, até achar ou acabar a lista.

A ideia é a mais natural possível: começar no primeiro elemento, comparar com o que se procura, e seguir para o próximo até achar ou até a lista acabar. O artigo sobre listas já mostrou o in e o index, e a busca linear é o que eles fazem por baixo. Escrevê-la à mão mostra o raciocínio:

def buscar(lista, alvo):
    for i in range(len(lista)):
        if lista[i] == alvo:
            return i
    return -1

nums = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91]
print(buscar(nums, 23), buscar(nums, 7), buscar(nums, 2), buscar(nums, 91))
print(23 in nums, 7 in nums, nums.index(23), nums.count(5))

def buscar_contando(lista, alvo):
    comparacoes = 0
    for i in range(len(lista)):
        comparacoes += 1
        if lista[i] == alvo:
            return i, comparacoes
    return -1, comparacoes

print(buscar_contando(nums, 23))
print(buscar_contando(nums, 2))
print(buscar_contando(nums, 7))

def todas(lista, alvo):
    pos = []
    for i, v in enumerate(lista):
        if v == alvo:
            pos.append(i)
    return pos
print(todas([4, 1, 4, 7, 4], 4), todas([4, 1, 4, 7, 4], 9))

alunos = [
    {"id": 101, "nome": "Ana"},
    {"id": 102, "nome": "Bruno"},
    {"id": 103, "nome": "Carla"}
]
def achar_aluno(codigo):
    for a in alunos:
        if a["id"] == codigo:
            return a["nome"]
    return None
print(achar_aluno(102), achar_aluno(999))

def primeiro_maior(lista, limite):
    for v in lista:
        if v > limite:
            return v
    return None
print(primeiro_maior(nums, 40), primeiro_maior(nums, 100))

maior = nums[0]
for v in nums:
    if v > maior:
        maior = v
print(maior)
print("fim")

A saída é 5 -1 0 9, depois True False 5 1, depois (5, 6), (0, 1) e (-1, 10), depois [0, 2, 4] [], depois Bruno None, depois 56 None, depois 91 e por fim fim.

A função buscar percorre os índices com range(len(lista)) e, ao achar o alvo, devolve a posição com return, o que encerra a função na hora. Se o laço termina sem achar, a execução chega ao return -1 depois dele. O -1 é a convenção para "não achei", a mesma do método find dos textos, porque não é uma posição que alguém procure. Repare no lugar do return -1: ele está fora do laço, alinhado com o for. Esse alinhamento é o detalhe mais importante da função, e voltaremos a ele na seção de erros.

A segunda linha compara com os recursos prontos. O 23 in nums responde só sim ou não, o nums.index(23) devolve a posição, e o nums.count(5) conta quantas vezes o valor aparece. Eles fazem a mesma busca linear sem que você escreva o laço, e são a primeira escolha quando basta saber se um valor existe. A diferença é que o index levanta um erro se o valor não estiver na lista, enquanto a função buscar devolve -1.

Contar as comparações

A função buscar_contando devolve duas coisas, a posição e o número de comparações feitas, com return i, comparacoes, que na verdade é uma tupla, como visto no artigo sobre tuplas e desempacotamento. Os resultados mostram o comportamento da busca linear:

  • Achar o 2, que é o primeiro, custa 1 comparação: o melhor caso.
  • Achar o 23, no meio da lista, custa 6.
  • Procurar o 7, que não existe, custa 10: o pior caso, em que a lista toda é percorrida para só então dizer que não há.

Uma busca linear numa lista de n elementos faz, no pior caso, n comparações. Para dez elementos, ninguém repara. Para um milhão, é um milhão de comparações a cada busca.

Variações da busca linear

O mesmo esqueleto resolve vários problemas parecidos, e vale reconhecê-lo:

  • Todas as posições. A função todas não para no primeiro achado. Ela guarda cada posição numa lista com append, e usa o enumerate para ter índice e valor. Sem ocorrências, devolve a lista vazia.
  • Busca em registros. O achar_aluno percorre uma lista de dicionários e compara um campo, o id. Devolve o nome do aluno ou None. É o padrão de qualquer "procure o cadastro com este código", e o artigo sobre dicionários mostrou como montar esses registros.
  • O primeiro que passa de um limite. O primeiro_maior devolve o valor, e não a posição, do primeiro elemento acima do limite.
  • O maior valor. Procurar o maior elemento também é uma busca linear: guarda-se o melhor visto até agora e atualiza-se quando aparece um maior. É o que os desafios Maior da Lista e Maior Número pedem, e o desafio Busca na Lista pede a busca de um valor lido do teclado.
FastMindAssistente de IA local

A IA que lembra da sua vida e mora no seu computador

  • Memória de longo prazo com a fonte de cada resposta
  • Funciona offline: seus dados ficam no seu PC
  • Lê PDFs, slides, fotos e gravações de reunião
Baixar grátis para Windows

Plano Essencial grátis para sempre

Busca binária: olhar o meio

A mesma lista de dez números em três linhas, uma para cada passo da busca binária pelo 23. No passo 1, esq 0, dir 9 e meio 4, o valor 16 é menor que 23 e a metade da esquerda é descartada. No passo 2, esq 5, dir 9 e meio 7, o 56 é maior e a metade da direita é descartada. No passo 3, esq 5, dir 6 e meio 5, o 23 é achado. Uma faixa diz que são 3 comparações contra 6 da busca linear.
A busca binária olha o meio da faixa e descarta metade das posições a cada passo.

Pense num dicionário de papel. Para achar uma palavra, ninguém começa na primeira página. Abre-se no meio, vê-se se a palavra procurada vem antes ou depois, e repete-se a ideia na metade certa. A busca binária faz exatamente isso, e só dá certo porque o dicionário está em ordem alfabética: sem ordem, olhar o meio não diz nada sobre onde está o resto.

def busca_binaria(lista, alvo):
    esq = 0
    dir = len(lista) - 1
    while esq <= dir:
        meio = (esq + dir) // 2
        if lista[meio] == alvo:
            return meio
        elif lista[meio] < alvo:
            esq = meio + 1
        else:
            dir = meio - 1
    return -1

nums = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91]
print(busca_binaria(nums, 23), busca_binaria(nums, 7), busca_binaria(nums, 2), busca_binaria(nums, 91))
print(busca_binaria([], 5), busca_binaria([5], 5), busca_binaria([5], 6))

def busca_binaria_passos(lista, alvo):
    esq = 0
    dir = len(lista) - 1
    passo = 0
    while esq <= dir:
        meio = (esq + dir) // 2
        passo += 1
        print("passo", passo, "esq", esq, "dir", dir, "meio", meio, "valor", lista[meio])
        if lista[meio] == alvo:
            return meio
        elif lista[meio] < alvo:
            esq = meio + 1
        else:
            dir = meio - 1
    return -1
print(busca_binaria_passos(nums, 23))
print(busca_binaria_passos(nums, 7))
print("fim")

A saída começa com 5 -1 0 9 e -1 0 -1. Depois vem o rastro da busca pelo 23: passo 1 esq 0 dir 9 meio 4 valor 16, passo 2 esq 5 dir 9 meio 7 valor 56, passo 3 esq 5 dir 6 meio 5 valor 23 e o resultado 5. Em seguida, o rastro da busca pelo 7: passo 1 esq 0 dir 9 meio 4 valor 16, passo 2 esq 0 dir 3 meio 1 valor 5, passo 3 esq 2 dir 3 meio 2 valor 8 e o resultado -1. Por fim, fim.

O algoritmo mantém dois marcadores, esq e dir, que delimitam a faixa onde o alvo ainda pode estar. No começo, a faixa é a lista inteira, da posição 0 até a última. A cada volta do laço:

  1. Calcula-se o meio da faixa, com a divisão inteira (esq + dir) // 2.
  2. Se o valor do meio é o alvo, a busca acabou: devolve-se meio.
  3. Se o valor do meio é menor que o alvo, o alvo só pode estar à direita, e a faixa passa a começar em meio + 1.
  4. Se é maior, o alvo só pode estar à esquerda, e a faixa passa a terminar em meio - 1.

O laço roda enquanto a faixa tiver pelo menos uma posição, esq <= dir. Quando esq passa de dir, a faixa ficou vazia e o alvo não está na lista, o que leva ao return -1. O rastro do passo mostra isso nos dois casos. Para o 23, a faixa vai de 10 posições para 5, depois para 2, e no terceiro passo o meio já é o alvo. Para o 7, que não existe, a faixa encolhe até esvaziar, e a busca conclui "não está" em três passos, enquanto a linear precisou de dez.

Os dois casos de borda aparecem na segunda linha da saída. Com a lista vazia, dir começa em -1, o laço nem roda e o resultado é -1. Com um elemento só, o meio é o próprio elemento. O -1 0 -1 mostra a lista vazia, o achado e o não achado.

O meio + 1 e o meio - 1 merecem atenção. Eles descartam o próprio meio, que já foi comparado. Se algum dos dois fosse escrito só como meio, a faixa poderia deixar de encolher, e o laço rodaria para sempre. É o tipo de erro que se descobre com um print do rastro, como o da função busca_binaria_passos.

Quanto trabalho cada busca dá

Tabela com o número de comparações no pior caso. Para 10 elementos, a busca linear faz 10 e a binária faz 4. Para 100, são 100 e 7. Para 1000, são 1000 e 10. Para 1 milhão, são 1 milhão e 20. À direita, dois quadros resumem quando usar cada busca: a binária pede lista ordenada e serve para listas grandes e muitas buscas, a linear serve para qualquer lista, pequena ou que muda toda hora.
No pior caso, a busca linear cresce junto com a lista e a binária cresce devagar, mas só vale em lista ordenada.

A vantagem da binária está no ritmo em que o trabalho cresce. A cada passo, a faixa cai pela metade, e então o número de passos no pior caso é quantas vezes é possível dividir a lista ao meio até sobrar nada. O código abaixo conta isso sem precisar criar listas enormes, simulando só os marcadores:

def passos_pior_caso(n):
    esq = 0
    dir = n - 1
    passos = 0
    while esq <= dir:
        meio = (esq + dir) // 2
        passos += 1
        esq = meio + 1
    return passos
for n in [10, 100, 1000, 1000000]:
    print(n, passos_pior_caso(n))
print(passos_pior_caso(0), passos_pior_caso(1), passos_pior_caso(2), passos_pior_caso(3), passos_pior_caso(4))
print("fim")

A saída é 10 4, 100 7, 1000 10, 1000000 20, depois 0 1 2 2 3 e por fim fim.

Para uma lista de 1000 elementos, a binária faz no máximo 10 comparações, e a linear, 1000. Para um milhão, são 20 contra um milhão. Cada vez que a lista dobra de tamanho, a binária faz um passo a mais, enquanto a linear faz o dobro de passos. Esse jeito de medir como o trabalho cresce com o tamanho dos dados é o assunto do último artigo desta parte, sobre complexidade de algoritmos, e aqui basta guardar a imagem: uma cresce em linha reta, a outra quase não se mexe.

A conta tem um preço. A binária só vale com a lista ordenada, e ordenar custa trabalho. Se a lista muda toda hora, ou se só se faz uma busca nela, ordenar antes para buscar depois sai mais caro do que uma busca linear simples. A binária compensa quando a lista é grande e ordenada uma vez, e as buscas são muitas. Os próximos artigos mostram como ordenar.

O que dá errado com a busca binária

A busca binária tem pré-requisitos que ela não verifica. O mais importante é a ordem:

def busca_binaria(lista, alvo):
    esq = 0
    dir = len(lista) - 1
    while esq <= dir:
        meio = (esq + dir) // 2
        if lista[meio] == alvo:
            return meio
        elif lista[meio] < alvo:
            esq = meio + 1
        else:
            dir = meio - 1
    return -1
desordenada = [9, 2, 7, 1, 5, 3]
print(busca_binaria(desordenada, 5), 5 in desordenada)
print(busca_binaria(desordenada, 9), 9 in desordenada)
print(busca_binaria(desordenada, 2), 2 in desordenada)
print(busca_binaria(sorted(desordenada), 5))
print(busca_binaria([1, 2, 2, 2, 3], 2), busca_binaria([1, 2, 2, 2, 2, 3], 2))
print(busca_binaria(["ana", "bia", "caio", "dora"], "caio"))
print("fim")

A saída é -1 True, -1 True, -1 True, depois 3, depois 2 2, depois 2 e por fim fim.

Os três primeiros resultados são o problema. Os valores 5, 9 e 2 estão na lista, como o in confirma com True, mas a busca binária disse -1 para os três. Ela olhou o meio, concluiu algo sobre a ordem que não existe, e foi procurar do lado errado. Não houve erro nem aviso: o programa só respondeu errado. Depois de passar a lista por sorted, a busca acha o 5 na posição 3 da lista ordenada, [1, 2, 3, 5, 7, 9].

O quinto resultado mostra o comportamento com valores repetidos. Nas listas [1, 2, 2, 2, 3] e [1, 2, 2, 2, 2, 3], a busca devolve a posição 2 nas duas, que é uma posição onde há um 2, mas não a primeira. A busca binária devolve uma das ocorrências, a que caiu no meio, e não garante a primeira nem a última. Se o problema pede a primeira, é preciso ajustar o algoritmo, ou usar a busca linear.

O último resultado mostra que a busca binária também funciona com textos, porque o < compara textos em ordem alfabética, como visto no artigo sobre strings. O requisito é que a lista esteja ordenada pelo mesmo critério da comparação.

FastTeacherPara professores

Da ementa ao semestre planejado

  • Plano de ensino e calendário montados sozinhos
  • Diário de aulas pronto para colar no SIGA
  • Slides e listas de exercícios com gabarito
Conhecer o FastTeacher

Versão Community grátis · Windows

Na ESP32: tabela de calibração, cartões e limites

from machine import ADC, Pin

TABELA = [(0, -10), (500, 0), (1200, 10), (2000, 20), (2800, 30), (3500, 40), (4095, 50)]

def faixa(leitura):
    esq = 0
    dir = len(TABELA) - 1
    while esq < dir:
        meio = (esq + dir + 1) // 2
        if TABELA[meio][0] <= leitura:
            esq = meio
        else:
            dir = meio - 1
    return esq

for lido in [0, 499, 500, 1500, 3499, 4095]:
    i = faixa(lido)
    print(lido, i, TABELA[i][1])

CARTOES = ["A1B2", "C3D4", "E5F6"]
for lido in ["C3D4", "ZZZZ"]:
    print(lido, "liberado" if lido in CARTOES else "negado")

leituras = [310, 295, 402, 515, 530, 498]
limite = 500
for i in range(len(leituras)):
    if leituras[i] > limite:
        print("primeira acima do limite:", i, leituras[i])
        break
else:
    print("nenhuma acima")
adc = ADC(Pin(34))
adc.atten(ADC.ATTN_11DB)
print(0 <= adc.read() <= 4095)
print("fim")

A saída é 0 0 -10, 499 0 -10, 500 1 0, 1500 2 10, 3499 4 30, 4095 6 50, depois C3D4 liberado e ZZZZ negado, depois primeira acima do limite: 3 515, depois True e fim.

O primeiro uso é uma tabela de calibração. A leitura do conversor analógico, de 0 a 4095, é convertida em temperatura por uma tabela de faixas, em que cada linha diz "a partir deste valor lido, vale esta temperatura". Os números do exemplo são inventados para ilustrar, e uma curva de verdade vem do manual do sensor ou de medições. O artigo sobre o ADC da ESP32 mostra por que a leitura não é linear.

A tabela está ordenada pela leitura, então a busca binária cabe. Só que aqui o objetivo é diferente: não se procura um valor exato, e sim a última faixa que começa antes da leitura. Por isso a função faixa muda dois detalhes: o meio é calculado com (esq + dir + 1) // 2, arredondando para cima, e o laço roda com esq < dir, sem o teste de igualdade. Quando a faixa do meio começa em um valor menor ou igual à leitura, esq passa a ser o próprio meio, que ainda pode ser a resposta. O + 1 no cálculo do meio garante que a faixa sempre encolhe. Com 7 linhas, a diferença entre as duas buscas é de poucos passos, e uma busca linear daria o mesmo resultado, mas em tabelas com centenas de linhas a binária compensa.

O segundo uso é a lista de cartões autorizados. Aqui o in resolve: a lista é curta, não está ordenada e muda quando alguém é cadastrado. Ordenar e usar a binária seria mais trabalho sem ganho. O exemplo só ilustra a busca; um controle de acesso de verdade pede muito mais cuidado, como guardar os códigos fora do programa.

O terceiro é achar o primeiro valor acima de um limite numa sequência de leituras. O laço para com break na primeira ocorrência, e o else do for, visto no artigo sobre break, continue e else, só roda quando o laço termina sem break, ou seja, quando nenhuma leitura passou do limite. As leituras chegam em ordem de tempo e não de valor, então só a linear faz sentido.

Por fim, as duas últimas linhas criam um ADC no pino 34 e confirmam que a leitura fica entre 0 e 4095. Elas não fazem parte da busca: mostram de onde viria o lido num circuito real.

Erros comuns

O primeiro erro é usar o index esperando um -1:

nums = [2, 5, 8]
print(nums.index(7))
ValueError: object not in sequence

No Python de computador, a mensagem é 7 is not in list. O index exige que o valor exista. A solução é testar antes com in, ou escrever a função buscar, que devolve -1. O erro é o mesmo quando se passa um ponto de partida que fica depois da única ocorrência.

O segundo é importar o bisect, que existe no Python de computador:

import bisect
print(bisect.bisect_left([1, 3, 5], 3))
ImportError: no module named 'bisect'

O simulador não tem esse módulo, e não se deve contar com ele na placa. A saída é escrever a busca à mão, como neste artigo.

O terceiro é comparar tipos diferentes na busca binária, como procurar o texto "3" numa lista de números:

def busca_binaria(lista, alvo):
    esq = 0
    dir = len(lista) - 1
    while esq <= dir:
        meio = (esq + dir) // 2
        if lista[meio] == alvo:
            return meio
        elif lista[meio] < alvo:
            esq = meio + 1
        else:
            dir = meio - 1
    return -1
print(busca_binaria([1, 3, 5], "3"))
TypeError: unsupported types for __lt__: 'int', 'str'

No Python de computador, o texto é '<' not supported between instances of 'int' and 'str'. A comparação de igualdade, ==, aceita tipos diferentes e só dá falso, mas o < não. Isso costuma acontecer quando o alvo vem de input(), que sempre devolve texto, e a lista tem números. A correção é converter o alvo com int().

O quarto é um erro de cálculo do meio com a divisão comum:

nums = [2, 5, 8, 12]
meio = (0 + 3) / 2
print(meio)
print(nums[meio])
1.5
TypeError: list indices must be integers, not float

O / devolve um número com vírgula, e a lista só aceita índice inteiro. É por isso que a busca usa o //. No Python de computador, a mensagem é list indices must be integers or slices, not float.

Armadilhas silenciosas

Alguns erros não geram mensagem nenhuma. O pior deles na busca linear é o return -1 no lugar errado:

def errada(lista, alvo):
    for i in range(len(lista)):
        if lista[i] == alvo:
            return i
        else:
            return -1
print(errada([5, 6, 7], 5), errada([5, 6, 7], 7))
def ok(lista, alvo):
    for i in range(len(lista)):
        if lista[i] == alvo:
            return i
    return -1
print(ok([5, 6, 7], 5), ok([5, 6, 7], 7))
print("fim")

A saída é 0 -1 e 0 2, e depois fim. A função errada devolve -1 logo no primeiro elemento que não for o alvo, e nunca olha os outros. Para o 5, que é o primeiro, dá certo, e é por isso que o teste simples passa. Para o 7, no fim da lista, dá -1, e o valor estava lá. O else colado ao if dentro do laço decide cedo demais. A regra é: só se pode dizer "não achei" depois de olhar todos, e isso é o que o return -1 fora do laço faz.

O outro é usar o resultado -1 como se fosse uma posição. Em Python, o índice -1 é válido, e é o último elemento. Com a lista nums de antes, nums[buscar(nums, 7)] dá 91, e não um erro. A busca disse "não achei", o programa ignorou e pegou o último elemento. Sempre se confere o resultado antes de usá-lo como índice.

Há ainda o laço da busca binária que perde o último candidato. Se a condição for esq < dir em vez de esq <= dir, a faixa de um elemento só nunca é examinada. Numa execução com essa troca, a busca devolve -1 para o 5 em [5], -1 para o 8 em [2, 5, 8] e -1 para o 12 em [2, 5, 8, 12], e só acerta o 5 em [2, 5, 8], onde o meio cai no valor certo. Quem testa só com o elemento do meio não vê o defeito.

O que costuma aparecer em sala

Estes são padrões que se repetem com quem aprende busca. Não são o relato de uma turma específica.

O return -1 dentro do laço. É o mais comum, como mostrado. A função passa no teste de achar o primeiro elemento e falha nos outros. O caminho de volta é testar sempre com o último elemento e com um valor que não existe.

Devolver o valor em vez da posição. Escrever return lista[i] ou return v onde se pedia a posição. Como a busca compara lista[i] com o alvo, o valor devolvido é igual ao alvo, e a resposta não diz nada de novo.

Usar o -1 como índice. Já descrito. A pessoa busca, guarda o resultado e acessa a lista sem verificar.

Busca binária em lista não ordenada. O resultado vem errado sem aviso, e a pessoa conclui que o algoritmo "não funciona". O hábito a criar é perguntar, antes de qualquer busca binária, quem garante a ordem.

Confundir a binária com "pular de dois em dois". Alguns imaginam que ela olha uma posição sim, outra não. Rodar a função com o rastro de passos, que imprime esq, dir e meio, resolve a dúvida melhor do que a explicação.

/ no lugar de //. Gera o erro de índice com número com vírgula, e quase sempre se resolve de primeira.

meio em vez de meio + 1. Em alguns casos o programa trava num laço que não termina. Colocar um contador de passos que interrompe o laço depois de, digamos, 50 voltas é uma boa rede de proteção durante os testes.

Só testar com o valor que existe. Os três testes mínimos são: um valor no começo, um no fim e um que não existe. Com isso, a maioria dos defeitos aparece.

Exercício: busca binária com contagem de passos

Escreva um programa que leia a quantidade n de elementos, depois n números já em ordem crescente, e por fim o valor a procurar. Ele deve mostrar a posição achada e em quantos passos, ou que o valor não foi encontrado, e comparar com a busca linear feita pelos recursos prontos. O bloco usa entradas= para fornecer a lista [3, 7, 11, 15, 19] e o alvo 15.

n = int(input())
lista = []
for i in range(n):
    lista.append(int(input()))
alvo = int(input())
esq = 0
dir = n - 1
passos = 0
achou = -1
while esq <= dir:
    meio = (esq + dir) // 2
    passos += 1
    if lista[meio] == alvo:
        achou = meio
        break
    elif lista[meio] < alvo:
        esq = meio + 1
    else:
        dir = meio - 1
if achou == -1:
    print("nao encontrado em", passos, "passos")
else:
    print("posicao", achou, "em", passos, "passos")
print("busca linear:", lista.index(alvo) if alvo in lista else -1)
print("fim")

O simulador repete na saída os valores digitados, um por linha, e depois vêm posicao 3 em 2 passos, busca linear: 3 e fim.

Na primeira volta, o meio é a posição 2, que vale 11 e é menor que 15, então a faixa passa a começar na posição 3. Na segunda, o meio é a posição 3, que vale 15, e a busca acaba. O break sai do laço assim que o valor é achado, e a variável achou guarda a posição, com -1 como valor inicial de "ainda não achei". A última linha usa o index guardado por um in, para nunca levantar o ValueError.

Para treinar além disso, tente: procurar um valor que não existe, como 10, e ver quantos passos a binária gasta; procurar o primeiro e o último elemento; acrescentar uma verificação de que a lista lida está mesmo em ordem, com um laço que compare cada elemento com o seguinte; e mostrar, junto, quantas comparações a busca linear teria feito.

Perguntas frequentes

Qual busca devo usar? Se a lista é pequena, não está ordenada, ou muda toda hora, use a linear, ou simplesmente o in e o index. Se a lista é grande, está ordenada e recebe muitas buscas, a binária compensa.

Preciso escrever a busca linear à mão se existe o in? Para saber só se o valor existe, o in basta. Escrever à mão é necessário quando a busca tem uma regra própria, como achar o primeiro valor acima de um limite, comparar um campo de um registro, ou contar as comparações.

A busca binária funciona em lista de textos? Funciona, desde que a lista esteja ordenada em ordem alfabética e o alvo seja um texto. O resultado depende da comparação de caracteres, e por isso letras maiúsculas e minúsculas se ordenam separadas.

O que faço se a lista tem valores repetidos? A busca binária devolve uma das posições, sem garantia de qual. Para achar a primeira, ajuste o algoritmo para continuar procurando à esquerda depois de um achado, ou use a busca linear.

Por que o simulador não tem bisect? Porque ele não existe no simulador, que imita o MicroPython. A busca escrita à mão é curta e serve nos dois lugares, no computador e na placa.

A busca binária pode ser escrita com recursão? Pode: a função chama a si mesma com uma faixa menor. Como a faixa cai pela metade, a profundidade fica pequena. O artigo sobre recursividade trata do assunto.

Para continuar

O desafio Busca na Lista pratica a busca linear com um valor lido do teclado, e os desafios Maior da Lista e Maior Número praticam a varredura que guarda o melhor valor visto. Para rever a base, vale passar pelos artigos sobre listas, operações com listas e matrizes, e pela trilha de lógica.

O próximo artigo trata da ordenação: o bubble sort e o selection sort, dois algoritmos simples que colocam uma lista em ordem, explicados passo a passo.

Para praticar no PyPlaca

Trilhas

Lições

Fichas de componentes

FastTeacherPara professores

Da ementa ao semestre planejado

  • Plano de ensino e calendário montados sozinhos
  • Diário de aulas pronto para colar no SIGA
  • Slides e listas de exercícios com gabarito
Conhecer o FastTeacher

Versão Community grátis · Windows

FastMindAssistente de IA local

A IA que lembra da sua vida e mora no seu computador

  • Memória de longo prazo com a fonte de cada resposta
  • Funciona offline: seus dados ficam no seu PC
  • Lê PDFs, slides, fotos e gravações de reunião
Baixar grátis para Windows

Plano Essencial grátis para sempre

FastTeacherPara professores

Da ementa ao semestre planejado

  • Plano de ensino e calendário montados sozinhos
  • Diário de aulas pronto para colar no SIGA
  • Slides e listas de exercícios com gabarito
Conhecer o FastTeacher

Versão Community grátis · Windows