Trilha de aprendizado · Nível 14 · Tutorial 5

Buscar em sequências ordenadas com bisect

Use busca binária para localizar posições e intervalos em sequências ordenadas, contabilizando também o custo de ordenar e inserir elementos.

  • Nível: Intermediário
  • Duração: 18 min
  • 8 passos
Buscar em sequências ordenadas com bisect

O que você vai percorrer

  1. Por que a ordenação permite reduzir a busca Entenda por que uma lista ordenada permite eliminar regiões inteiras durante uma busca por limites. 2 min
  2. Encontrar os limites com bisect_left e bisect_right Localize os espaços de inserção antes e depois de valores equivalentes em uma lista ordenada. 3 min
  3. Transformar uma posição em teste de presença Use a posição candidata retornada por bisect_left para testar presença com segurança. 2 min
  4. Contar repetições e delimitar faixas Use os dois limites de busca para contar valores repetidos e recortar faixas com contratos inclusivos ou exclusivos. 3 min
  5. Manter a lista ordenada com insort Insira valores sem quebrar a ordenação e distinga o custo de localizar a posição do custo de deslocar elementos na lista. 2 min
  6. Contabilizar preparação, consultas e resultados Estime o custo completo de preparar uma sequência ordenada, consultá-la e devolver resultados. 2 min
  7. Escolher a estratégia pelo padrão de uso Compare varredura, índices hash e listas ordenadas com bisect de acordo com as consultas, atualizações e resultados exigidos. 2 min
  8. Aplicação final: consultar e atualizar uma sequência Execute um script autocontido para validar presença, contagens e faixas em uma lista ordenada, depois atualize-a sem perder a ordenação. 3 min

O que você vai aprender

  • Determinar limites de inserção com bisect_left e bisect_right.
  • Verificar a presença de um valor sem confundir posição de inserção com correspondência.
  • Delimitar ocorrências repetidas ou intervalos de valores.
  • Escolher entre varredura, índice hash e sequência ordenada conforme o padrão de consultas e alterações.

Antes de começar

  • Estimar tempo e memória com notação O grande
  • Medir trechos de código com timeit
  • Reduzir buscas repetidas com índices em memória
  • Passar funções como argumentos

Passo 1 de 8

Por que a ordenação permite reduzir a busca

Entenda por que uma lista ordenada permite eliminar regiões inteiras durante uma busca por limites.

A comparação que elimina candidatos

Metade deixa de importar

Em uma lista de números em ordem crescente, o elemento central divide os candidatos em duas regiões previsíveis. Ao comparar o alvo com esse elemento, uma das metades pode ser descartada com segurança.

Exemplo: se o alvo é menor que o valor central, tudo que está à direita dele também é grande demais. Se o alvo é maior, os valores à esquerda são pequenos demais. A busca continua apenas no intervalo que ainda pode conter a posição procurada.

Intervalo reduzido

O alvo 18 é menor que o valor central 21. Por isso, 21 e todos os valores à direita saem da busca; somente a região à esquerda permanece candidata.

Diagrama de uma lista crescente com os valores 3, 8, 12, 17, 21, 25 e 30; 21 é o centro destacado, e a região de 21 a 30 está descartada para procurar o alvo 18.

Em uma sequência crescente, comparar com o centro permite eliminar uma região inteira.

A condição indispensável

Ordenada pelo critério da comparação

Esse descarte só é válido se a sequência já estiver ordenada pelo mesmo critério usado na comparação. Neste tutorial, usaremos listas numéricas em ordem crescente, como [3, 8, 12, 17, 21].

O módulo bisect trabalha sobre essa condição: ele não ordena a lista e não verifica se ela realmente está ordenada. Em uma lista fora de ordem, o resultado pode não representar a posição esperada.

Exemplo

Comparação segura e comparação inválida

Lista ordenada: [4, 9, 15, 22, 31]
Alvo: 12; centro: 15
Como 12 < 15, os valores 15, 22 e 31 podem ser descartados.

Lista fora de ordem: [4, 22, 9, 15, 31]
O centro não divide valores menores e maiores de forma confiável. Descartar uma metade poderia eliminar o 9, que ainda é relevante para localizar o limite de 12.

Dica

O que torna a busca rápida

Em listas, acessar o elemento central por índice é O(1). Ao reduzir repetidamente o intervalo de candidatos pela metade, a localização de um limite custa O(log n), sem contar o trabalho necessário para preparar ou ordenar os dados.

Decida o que permanece

Qual região pode ser descartada?

Na lista ordenada [2, 7, 11, 19, 24, 29, 35], o elemento central observado é 19 e o alvo é 26. Qual região pode ser descartada com segurança nessa comparação?

Passo 2 de 8

Encontrar os limites com bisect_left e bisect_right

Localize os espaços de inserção antes e depois de valores equivalentes em uma lista ordenada.

Dois limites para o mesmo alvo

Antes ou depois das equivalências

Importe as funções do módulo padrão bisect:

from bisect import bisect_left, bisect_right

Em uma lista numérica ordenada:

  • bisect_left(lista, alvo) devolve o espaço antes do primeiro valor igual ao alvo. À esquerda ficam valores menores; à direita, valores maiores ou iguais.
  • bisect_right(lista, alvo) devolve o espaço depois do último valor igual ao alvo. À esquerda ficam valores menores ou iguais; à direita, valores maiores.

As duas funções retornam uma posição de inserção; elas não alteram a lista.

Espaços entre os elementos

Considere idades = [18, 21, 21, 21, 30] e alvo 21.

Diagrama de uma lista ordenada com os valores 18, 21, 21, 21 e 30; o limite esquerdo está no espaço antes do primeiro 21, na posição 1, e o limite direito está no espaço após o último 21, na posição 4.

Para o alvo 21, o limite esquerdo é 1 e o direito é 4. Posições indicam espaços, não necessariamente elementos existentes.

Observe as posições retornadas

Executando as duas buscas

As posições podem coincidir quando não há um valor equivalente.

python
from bisect import bisect_left, bisect_right

idades = [18, 21, 21, 21, 30]

print(bisect_left(idades, 21))   # 1
print(bisect_right(idades, 21))  # 4

print(bisect_left(idades, 25))   # 4
print(bisect_right(idades, 25))  # 4

print(bisect_left(idades, 10))   # 0
print(bisect_right(idades, 40))  # 5

Exemplo

Leia posições como espaços

Em idades = [18, 21, 21, 21, 30]:

  • Para 21, as posições são 1 e 4: há valores equivalentes entre esses dois espaços.
  • Para 25, ambas são 4: o valor entraria entre 21 e 30.
  • Para 10, ambas são 0: o valor entraria antes do primeiro elemento.
  • Para 40, ambas são 5, que é len(idades): o valor entraria após o último elemento.

Logo, o resultado sempre pode estar de 0 até len(lista), inclusive. Em [], ambas as funções retornam 0.

Preveja os limites

Limite esquerdo com repetição

Para numeros = [1, 4, 6, 7, 7, 9], bisect_left(numeros, 7) retorna ____.

Limite direito com repetição

Para numeros = [1, 4, 6, 7, 7, 9], bisect_right(numeros, 7) retorna ____.

Posição não é confirmação de existência

O que aconteceu com o 8?

Para numeros = [1, 4, 6, 7, 7, 9], tanto bisect_left(numeros, 8) quanto bisect_right(numeros, 8) retornam 5. Qual interpretação está correta?

Resumo

Essencial

  • bisect_left encontra o espaço antes dos valores equivalentes; bisect_right, o espaço depois deles.
  • Os retornos vão de 0 a len(lista), inclusive; em uma lista vazia, ambos retornam 0.
  • As funções não inserem, não modificam a lista e não confirmam sozinhas a existência do alvo.

Passo 3 de 8

Transformar uma posição em teste de presença

Use a posição candidata retornada por bisect_left para testar presença com segurança.

Posição candidata não é confirmação

Do limite à presença

bisect_left(valores, alvo) retorna a primeira posição onde o alvo poderia ficar. Isso ainda não prova que ele está na lista.

Para confirmar a presença, use duas condições:

  1. a posição deve existir na lista (posicao < len(valores));
  2. o elemento nessa posição deve ser igual ao alvo (valores[posicao] == alvo).

Dois resultados possíveis

A mesma posição candidata pode apontar para uma ocorrência real ou apenas para o local de inserção.

Comparação entre uma lista ordenada em que a posição candidata contém o alvo e outra em que a posição candidata contém um valor maior.

Quando o alvo não existe, o limite esquerdo pode ser uma posição válida, mas o elemento nela será diferente do alvo.

Uma função segura

Teste exato com bisect_left

A ordem das condições evita acessar um índice inexistente.

python
from bisect import bisect_left


def contem(valores: list[int], alvo: int) -> bool:
    posicao = bisect_left(valores, alvo)
    return posicao < len(valores) and valores[posicao] == alvo


idades = [18, 25, 25, 31, 42]
print(contem(idades, 25))  # True
print(contem(idades, 30))  # False
print(contem(idades, 99))  # False
print(contem([], 25))      # False

Atenção

Não inverta as condições

Escrever valores[posicao] == alvo and posicao < len(valores) é inseguro: quando posicao for igual a len(valores), o acesso ocorrerá antes da verificação e poderá gerar IndexError.

Com and, Python só avalia a segunda condição se a primeira for verdadeira. Por isso, verifique o limite antes de acessar a lista.

Monte a verificação

Ordem segura

Organize as etapas para verificar se alvo está em uma lista ordenada usando bisect_left.

  1. Verificar se `posicao < len(valores)`.
  2. Calcular `posicao = bisect_left(valores, alvo)`.
  3. Comparar `valores[posicao] == alvo` somente se a posição for válida.

Confira os casos-limite

Posição intermediária

Na lista [10, 20, 40], bisect_left(..., 30) retorna uma posição válida. Portanto, 30 está presente na lista.

Resumo

Regra para presença exata

  • Use bisect_left para encontrar a primeira posição candidata.
  • Uma posição candidata pode ser len(valores); não a acesse antes de validá-la.
  • Presença exata exige limite válido e igualdade: posicao < len(valores) and valores[posicao] == alvo.
  • Essa regra trata lista vazia, alvo entre elementos e alvo maior que todos os valores.

Passo 4 de 8

Contar repetições e delimitar faixas

Use os dois limites de busca para contar valores repetidos e recortar faixas com contratos inclusivos ou exclusivos.

Dois limites delimitam um bloco

Ocorrências consecutivas

Em uma lista ordenada, todas as ocorrências de um mesmo valor ficam juntas. Para o alvo 8, bisect_left encontra o início desse bloco e bisect_right encontra a posição logo após seu fim.

Assim, se esquerda = bisect_left(valores, 8) e direita = bisect_right(valores, 8), a quantidade de ocorrências é direita - esquerda. Não é preciso criar uma fatia apenas para contar.

Limites ao redor das repetições

Os marcadores mostram as posições de inserção que envolvem todas as ocorrências de 8.

Sequência ordenada 2, 5, 8, 8, 8, 11, 14, com um marcador esquerdo antes do primeiro 8 e um marcador direito depois do último 8.

O intervalo de índices [esquerda, direita) contém exatamente as três ocorrências de 8.

Contar sem materializar resultados

A diferença entre os limites é a contagem.

python
from bisect import bisect_left, bisect_right

valores = [2, 5, 8, 8, 8, 11, 14]
alvo = 8

esquerda = bisect_left(valores, alvo)
direita = bisect_right(valores, alvo)
quantidade = direita - esquerda

print(quantidade)  # 3

Faixas: defina o contrato dos extremos

Inclusivo ou exclusivo

Para devolver valores entre minimo e maximo, os limites dependem do contrato:

  • minimo <= valor <= maximo: use bisect_left no mínimo e bisect_right no máximo.
  • minimo <= valor < maximo: use bisect_left nos dois extremos.

A fatia sempre exclui seu índice final. O uso de bisect_right no máximo inclusivo faz a fatia terminar depois das ocorrências desse máximo.

Exemplo

Mesmo início, finais diferentes

from bisect import bisect_left, bisect_right

valores = [2, 5, 8, 8, 8, 11, 14]

# Contrato: 5 <= valor <= 8
inicio = bisect_left(valores, 5)   # 1
fim = bisect_right(valores, 8)     # 5
print(valores[inicio:fim])         # [5, 8, 8, 8]

# Contrato: 5 <= valor < 8
inicio = bisect_left(valores, 5)   # 1
fim = bisect_left(valores, 8)      # 2
print(valores[inicio:fim])         # [5]

Dica

Resultado vazio é válido

Se os dois limites coincidirem, a fatia é vazia: valores[p:p] resulta em []. Defina e valide o contrato da consulta para que minimo <= maximo; se o mínimo for maior que o máximo, os limites deixam de representar uma faixa válida.

Pratique: limites e custos

Associe o contrato à expressão

Considere inicio = bisect_left(valores, minimo). Associe cada objetivo ao limite final adequado.

Toque em um item e depois no par correspondente.

Calcule a contagem

Para valores = [1, 4, 4, 4, 9], bisect_left(valores, 4) retorna 1 e bisect_right(valores, 4) retorna 4. Complete: quantidade = 4 - 1 = ____.

Custo da consulta

Uma lista ordenada tem n elementos. Uma faixa retorna k valores. Qual afirmação está correta?

Passo 5 de 8

Manter a lista ordenada com insort

Insira valores sem quebrar a ordenação e distinga o custo de localizar a posição do custo de deslocar elementos na lista.

Inserir no próprio lugar

Busca e inserção em uma chamada

Além de localizar limites, o módulo bisect oferece insort_left e insort_right. Elas encontram a posição adequada e inserem o valor na própria lista; o retorno é None.

Use insort_left para inserir antes dos valores equivalentes e insort_right para inserir depois deles. As duas preservam a ordem crescente, desde que a lista já esteja ordenada pelo mesmo critério.

Antes ou depois dos equivalentes

As duas variantes diferem apenas no ponto escolhido dentro de um bloco de valores iguais.

Diagrama de uma lista ordenada com os valores 2, 5, 5 e 8. Uma seta de insort_left coloca um novo 5 antes dos dois cincos existentes; outra seta de insort_right o coloca depois deles.

Com valores iguais, left escolhe o limite esquerdo; right escolhe o limite direito.

Duas inserções com o mesmo valor

Execute o exemplo e observe que a lista é alterada após cada chamada.

python
from bisect import insort_left, insort_right

valores = [2, 5, 5, 8]

insort_left(valores, 5)
print(valores)  # [2, 5, 5, 5, 8]

insort_right(valores, 5)
print(valores)  # [2, 5, 5, 5, 5, 8]

O custo inclui deslocar elementos

Localizar não é inserir

A busca binária usada para encontrar o limite custa O(log n). Porém, uma lista é contígua: para abrir espaço no meio ou no início, os elementos à direita precisam ser deslocados.

Por isso, insort_left e insort_right têm custo O(n) no pior caso. A etapa de busca é rápida, mas o deslocamento pode envolver quase toda a lista.

Abrir espaço no meio

Inserir 4 antes de 5 exige mover 5, 7 e 9 uma posição para a direita.

Diagrama sequencial mostrando a lista ordenada 1, 3, 5, 7, 9; o valor 4 sendo inserido entre 3 e 5; e o resultado 1, 3, 4, 5, 7, 9, com setas mostrando 5, 7 e 9 deslocados para a direita.

O deslocamento dos elementos, e não a localização do índice, domina o pior caso.

Dica

Use a operação adequada

Se a identidade ou a ordem relativa entre itens equivalentes importar, escolha conscientemente insort_left ou insort_right. Para números iguais isolados, a lista resultante terá os mesmos valores visíveis, mas a posição escolhida ainda é diferente.

Preserve a pré-condição

Atenção

Não acrescente valores arbitrariamente

valores.append(4) em uma lista como [1, 3, 5] produz [1, 3, 5, 4], que deixou de estar ordenada. Depois disso, resultados de bisect_left, bisect_right e insort não têm a garantia esperada.

Exemplo

Atualização que mantém a regra

from bisect import bisect_left, insort_right

valores = [1, 3, 5]
insort_right(valores, 4)
print(valores)               # [1, 3, 4, 5]
print(bisect_left(valores, 4))  # 2

Como a inserção preservou a ordenação, a busca posterior continua válida.

Verifique a operação e o custo

Qual é o custo no pior caso?

Uma lista ordenada tem n elementos. Qual afirmação explica corretamente o custo de insort_left(lista, valor) ao inserir perto do início?

Escolha a variante

Você quer inserir um novo registro com chave numérica 10 depois de todos os registros que já têm chave 10, mantendo a lista ordenada. Qual chamada é adequada?

Passo 6 de 8

Contabilizar preparação, consultas e resultados

Estime o custo completo de preparar uma sequência ordenada, consultá-la e devolver resultados.

O custo começa antes da busca

Preparar também é trabalho

O custo de bisect só descreve a localização de um limite em uma sequência que já está ordenada. Se os dados chegam sem ordem, inclua a preparação no cenário.

duracoes_ordenadas = sorted(duracoes)

Para n elementos, sorted custa O(n log n) no pior caso. Como ela cria uma nova lista, também requer O(n) de armazenamento adicional para essa lista ordenada.

Da entrada sem ordem à sequência consultável

A ordenação é uma etapa anterior às consultas, não um detalhe gratuito da busca.

Diagrama mostrando uma lista de números fora de ordem sendo transformada em uma nova lista crescente; várias consultas apontam para a lista ordenada.

Quando a entrada não está ordenada, contabilize a criação da sequência ordenada antes de chamar bisect.

Exemplo

Mesmo resultado, custos incluídos diferentes

Se você ordenar duracoes uma vez e fizer várias consultas depois, a preparação pode ser compartilhada.

Já se cada uso recebe dados não ordenados e executa sorted(duracoes) de novo, o custo O(n log n) volta a fazer parte de cada uso. A fronteira da análise deve corresponder ao trabalho que o programa realmente executa.

Reutilizar limites e contabilizar saídas

m consultas sobre a mesma preparação

Sem atualizações e sem criar fatias, m consultas de limites em uma lista preparada custam:

O(n log n + m log n)

O primeiro termo é a ordenação inicial; o segundo reúne as m localizações com bisect. Se a lista já chega ordenada pelo critério correto, o termo de preparação não entra nessa operação.

Exemplo

Localizar não é materializar

Considere uma faixa delimitada assim:

inicio = bisect_left(valores, minimo)
fim = bisect_right(valores, maximo)
faixa = valores[inicio:fim]

Encontrar inicio e fim custa O(log n). Porém, criar faixa copia os elementos devolvidos. Se há k elementos na faixa, essa materialização custa O(k) de tempo e requer O(k) de memória adicional.

Em várias consultas que retornam fatias, inclua o total de elementos copiados nas saídas; não basta contar as buscas binárias.

Dica

Separe localização de resultado

Se você só precisa dos índices inicio e fim, não crie a fatia. Assim, a consulta permanece na etapa de localização; a cópia dos resultados só existe quando ela é necessária.

Avalie a carga completa

Atualizações também contam

insort_left e insort_right encontram a posição em O(log n), mas inserir em uma lista pode deslocar elementos à direita. No pior caso, cada inserção custa O(n).

Portanto, muitas inserções acumulam custos de deslocamento. Manter a lista ordenada não torna as atualizações automaticamente logarítmicas.

Relacione fase e custo

Associe cada fase ao custo predominante descrito.

Toque em um item e depois no par correspondente.

Comparação completa

Se uma consulta recebe uma lista não ordenada e devolve uma fatia de resultados, é suficiente comparar apenas o O(log n) das chamadas a bisect.

Passo 7 de 8

Escolher a estratégia pelo padrão de uso

Compare varredura, índices hash e listas ordenadas com bisect de acordo com as consultas, atualizações e resultados exigidos.

Comece pelo resultado que a consulta precisa

A estratégia depende da pergunta

Não existe uma estrutura sempre mais rápida. Primeiro preserve o contrato da consulta: você precisa apenas saber se um valor existe, obter um registro por chave, localizar sua posição ordenada ou trazer todos os valores de uma faixa?

Depois considere a carga completa: preparação, memória extra, quantidade de consultas, atualizações e tamanho dos resultados devolvidos.

Três caminhos para consultar dados

Comparação visual entre varrer uma lista, consultar um índice hash por chave e localizar uma faixa em uma sequência numérica ordenada.

A forma da consulta orienta a escolha mais do que o nome da estrutura.

Quando cada opção tende a fazer sentido

Exemplo

Padrões de uso

  • Varredura: uma única busca por um pedido em uma lista pequena ou pouco reutilizada. Evita preparar estruturas que talvez não sejam usadas de novo.
  • Índice hash (set ou dict): muitas consultas exatas repetidas, como verificar se um código existe ou associar um ID ao respectivo registro. Há custo de construção, memória adicional e manutenção após mudanças.
  • Sequência ordenada + bisect: consultas repetidas de posição, contagem ou faixa, como durações entre 30 e 60 minutos. Ordenar custa; localizar limites custa O(log n); inserir em uma lista ainda pode deslocar elementos em O(n).

Dica

Inclua a saída na conta

Uma faixa pode ser localizada com dois limites em O(log n), mas criar a fatia com k elementos adiciona O(k) de tempo e memória para os resultados. Muitas atualizações também podem tornar a manutenção da lista ordenada cara.

Associe o padrão à estratégia

Qual é a candidata mais adequada?

Relacione cada cenário à estratégia que tende a ser a melhor candidata.

Toque em um item e depois no par correspondente.

Justifique pela carga completa

Escolha para um caso realista

Uma aplicação mantém durações inteiras de atendimentos. Ela recebe poucas novas durações por dia, mas faz muitas consultas como “quantos atendimentos ficaram entre 20 e 35 minutos?” e às vezes exibe a lista desses atendimentos. Qual estratégia você escolheria e por quê?

Escreva pelo menos 80 caracteres (0/80).

Passo 8 de 8

Aplicação final: consultar e atualizar uma sequência

Execute um script autocontido para validar presença, contagens e faixas em uma lista ordenada, depois atualize-a sem perder a ordenação.

Execute uma solução completa

Fluxo da aplicação

Neste exemplo, as durações são preparadas uma vez com sorted. As consultas usam limites binários; só a faixa solicitada é materializada como uma nova lista. Copie o script para um arquivo .py no seu computador e execute-o.

Script autocontido

Dados, consultas, verificações e atualização em um único arquivo.

python
from bisect import bisect_left, bisect_right, insort_right


def presente(valores, alvo):
    posicao = bisect_left(valores, alvo)
    return posicao < len(valores) and valores[posicao] == alvo


def contar(valores, alvo):
    return bisect_right(valores, alvo) - bisect_left(valores, alvo)


def faixa_inclusiva(valores, minimo, maximo):
    if minimo > maximo:
        raise ValueError("o mínimo não pode ser maior que o máximo")

    inicio = bisect_left(valores, minimo)
    fim = bisect_right(valores, maximo)
    return valores[inicio:fim]


# Preparação: sorted cria uma nova lista ordenada.
duracoes_originais = [60, 30, 90, 15, 30, 45]
duracoes = sorted(duracoes_originais)

# Presença: posição candidata + limite válido + igualdade.
assert presente([], 30) is False
assert presente(duracoes, 30) is True
assert presente(duracoes, 50) is False
assert presente(duracoes, 120) is False

# Repetições e faixas, incluindo extremos e resultado vazio.
assert contar(duracoes, 30) == 2
assert contar(duracoes, 50) == 0
assert faixa_inclusiva(duracoes, 15, 15) == [15]
assert faixa_inclusiva(duracoes, 20, 60) == [30, 30, 45, 60]
assert faixa_inclusiva(duracoes, 91, 120) == []

print("Antes da inserção:", duracoes)
print("Faixa de 20 a 60:", faixa_inclusiva(duracoes, 20, 60))

# Atualização: insort_right encontra o limite e insere na própria lista.
insort_right(duracoes, 30)

assert duracoes == [15, 30, 30, 30, 45, 60, 90]
assert contar(duracoes, 30) == 3
assert faixa_inclusiva(duracoes, 20, 60) == [30, 30, 30, 45, 60]

print("Depois da inserção:", duracoes)
print("Ocorrências de 30:", contar(duracoes, 30))
print("Faixa de 20 a 60:", faixa_inclusiva(duracoes, 20, 60))

Dica

O que observar

Se nenhuma mensagem de erro aparecer, todos os assert passaram. A lista inicial é preparada com ordenação; depois, insort_right preserva essa condição ao inserir outro 30 após os equivalentes.

Leia os limites e o resultado da atualização

Antes e depois da inserção

A consulta de faixa encontra dois índices; a fatia usa o início inclusivo e o fim exclusivo.

Diagrama de uma lista ordenada de durações antes e depois de inserir 30. A faixa de 20 a 60 começa antes do primeiro 30 e termina depois do 60; após a inserção, há três valores 30 e a faixa cresce.

Os limites localizam a faixa; a fatia materializa apenas os elementos entre eles.

Interprete o comportamento

bisect_left produz uma posição onde o alvo poderia começar, não uma prova de presença. Por isso presente confirma duas condições: a posição existe na lista e o elemento nessa posição é igual ao alvo. Já contar não cria uma fatia: calcula somente direita - esquerda.

Atenção

Custo completo importa

A busca pelos limites custa O(log n), mas sorted custa O(n log n) quando os dados chegam desordenados. A inserção em uma lista pode deslocar elementos e custar O(n). E a fatia retornada por faixa_inclusiva custa O(k) em tempo e memória adicional para os k resultados.

Revisão e decisão de uso

Relate sua execução

Execute o script e relate o que ocorreu com a faixa de 20 a 60 antes e depois da inserção. Em seguida, explique por que uma posição de bisect_left não basta para afirmar presença e justifique quando esta estratégia é adequada.

Escreva pelo menos 180 caracteres (0/180).

Resumo

Síntese do tutorial

Use a estrutura que atende ao resultado exigido pelo padrão real de uso.

  • Prepare uma sequência ordenada pelo mesmo critério das consultas antes de aplicar bisect.
  • Use bisect_left e uma verificação de limite mais igualdade para testar presença com segurança.
  • Combine os limites para contar repetições ou delimitar faixas; uma fatia materializa k resultados.
  • insort_left e insort_right preservam a ordem, mas a inserção em lista pode deslocar elementos em O(n).
  • Considere a carga inteira: ordenação inicial, número e tipo de consultas, atualizações, memória e tamanho das saídas.
  • Prefira sequência ordenada para consultas frequentes por posição ou faixa com poucas alterações; avalie varredura ou hash em outros padrões.

Tutorial concluído

Parabéns! Você concluiu: Buscar em sequências ordenadas com bisect

Muito bem! Agora você consegue decidir quando uma sequência ordenada com bisect compensa e contabilizar os custos além da busca binária.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso