bisect en Python: listas ordenadas

Publicado el: 08/08/2026
Tempo de leitura: 5 minutos
Búsqueda binaria y listas ordenadas con bisect en Python

El módulo bisect de la biblioteca estándar resuelve un problema concreto: encontrar rápidamente la posición correcta de un valor dentro de una lista que ya está ordenada. En lugar de recorrer todos los elementos, aplica búsqueda binaria y descarta la mitad del intervalo restante en cada comparación. Es una herramienta útil para rankings, bandas de precio, umbrales, horarios, catálogos y colecciones en memoria con muchas lecturas.

En esta guía aprenderás a usar bisect_left, bisect_right, insort_left e insort_right, además de comprender sus costos reales, el tratamiento de duplicados, el parámetro key y los errores más comunes.

Por qué importa la búsqueda binaria

Una búsqueda lineal puede revisar muchos elementos. La búsqueda binaria necesita muchas menos comparaciones porque reduce el espacio de búsqueda de forma exponencial. Encontrar una posición es una operación logarítmica. Insertar en una lista de Python sigue siendo lineal, porque las referencias posteriores pueden necesitar desplazarse.

Por eso, bisect funciona especialmente bien cuando hay muchas consultas y pocas inserciones. Una base de datos, un árbol balanceado, un heap o una colección ordenada especializada puede ser mejor para cargas con cambios constantes.

bisect_left y bisect_right

from bisect import bisect_left, bisect_right

valores = [10, 20, 20, 20, 30, 40]

inicio = bisect_left(valores, 20)
fin = bisect_right(valores, 20)

print(inicio)  # 1
print(fin)     # 4
print(valores[inicio:fin])

bisect_left devuelve la primera posición válida para el objetivo. bisect_right devuelve la posición inmediatamente posterior a todos los valores iguales. Juntas, estas funciones permiten contar duplicados, extraer intervalos iguales y elegir si un nuevo elemento debe aparecer antes o después de sus equivalentes.

Insertar sin perder el orden

from bisect import insort_left, insort_right

notas = [5.0, 6.5, 8.0, 9.5]
insort_left(notas, 8.0)
insort_right(notas, 8.0)
print(notas)

Las funciones insort combinan la localización de la posición y la inserción. El código queda más claro que una búsqueda manual seguida de list.insert. La búsqueda es rápida, pero el movimiento de elementos sigue dominando el costo de la operación.

Buscar un valor exacto

from bisect import bisect_left

def buscar_exacto(lista, objetivo):
    posicion = bisect_left(lista, objetivo)
    if posicion != len(lista) and lista[posicion] == objetivo:
        return posicion
    return -1

El índice devuelto es solo una posición de inserción. No demuestra que el elemento exista. Siempre valida el límite y compara el valor antes de considerar la búsqueda exitosa.

Crear tablas de rangos

from bisect import bisect_right

limites = [100, 250, 500, 1000]
etiquetas = ["mínimo", "pequeño", "medio", "grande", "empresa"]

def clasificar(valor):
    return etiquetas[bisect_right(limites, valor)]

Este patrón convierte límites ordenados en una tabla de clasificación rápida. Puede aplicarse a tarifas de envío, niveles de precio, categorías de latencia, impuestos, puntuaciones y reglas de servicio.

Usar el parámetro key

Las versiones modernas de Python permiten usar una función key para extraer la clave de comparación de cada elemento almacenado. La clave se aplica a los elementos de la lista, pero no al valor buscado. Por eso, el argumento x debe ser una clave compatible.

from bisect import bisect_left

productos = [
    {"nombre": "A", "precio": 10},
    {"nombre": "B", "precio": 25},
    {"nombre": "C", "precio": 40},
]

posicion = bisect_left(productos, 30, key=lambda item: item["precio"])
print(posicion)

Las búsquedas repetidas pueden ejecutar la función de clave muchas veces. Si calcularla es costoso, conviene mantener una lista paralela con claves precalculadas o aplicar caché.

Listas paralelas de claves

from bisect import bisect_left

registros = [
    {"id": 101, "nombre": "Ana"},
    {"id": 205, "nombre": "Bruno"},
    {"id": 330, "nombre": "Carla"},
]
ids = [registro["id"] for registro in registros]

posicion = bisect_left(ids, 205)
if posicion < len(ids) and ids[posicion] == 205:
    print(registros[posicion])

Este diseño evita recalcular la clave. Su riesgo principal es la sincronización: inserciones, eliminaciones y cambios deben modificar ambas listas en posiciones equivalentes. Encapsularlas en una clase ayuda a proteger esa regla.

Errores comunes

El primer error es usar bisect sobre datos desordenados. El módulo no valida el orden y puede devolver un índice aparentemente razonable pero incorrecto. El segundo error es creer que la inserción también es logarítmica. La búsqueda lo es; la modificación de la lista es lineal. El tercero es tratar la posición de inserción como prueba de existencia. El cuarto es ordenar con un criterio y buscar con otro.

La concurrencia introduce otro riesgo. Una búsqueda seguida de una inserción no debe considerarse atómica cuando varios threads o procesos modifican la misma colección. Protege la operación con sincronización adecuada o mueve los datos a un almacenamiento transaccional.

Cuándo usar bisect

Es una buena opción para tablas pequeñas y medianas en memoria, umbrales estáticos, valores de configuración ordenados, catálogos con muchas lecturas, límites temporales e índices ligeros. No conviene usarlo como sustituto de un índice de base de datos en conjuntos grandes y muy cambiantes, ni como cola de prioridad con eliminaciones continuas.

Ejemplo completo: ranking ordenado

from bisect import bisect_left, bisect_right

ranking = []
puntos = []

def agregar(nombre, puntuacion):
    posicion = bisect_right(puntos, puntuacion)
    puntos.insert(posicion, puntuacion)
    ranking.insert(posicion, {"nombre": nombre, "puntos": puntuacion})

def superiores_a(minimo):
    posicion = bisect_left(puntos, minimo)
    return ranking[posicion:]

El ejemplo separa las claves de puntuación de los registros. Esto mejora las búsquedas repetidas, pero exige mantener ambas listas sincronizadas. En producción, oculta la estructura detrás de una clase y prueba listas vacías, duplicados, límites, eliminaciones y actualizaciones.

Buenas prácticas

Documenta el criterio de ordenación, valida los datos de entrada, prueba duplicados y extremos, mide el costo cuando hay muchas inserciones y encapsula las invariantes. También puedes consultar las guías de Academify sobre difflib, fractions, statistics, types y linecache.

Como referencias oficiales, revisa la documentación de bisect y el tutorial de estructuras de datos.

Conclusión

bisect es una solución pequeña, predecible y eficiente para encontrar posiciones en listas ordenadas. Su mayor ventaja es la búsqueda rápida con muy poco código. Cuando la carga tiene muchas lecturas y pocas escrituras, suele ser una excelente opción de la biblioteca estándar. Cuando predominan las actualizaciones, mide el costo real y considera una estructura especializada.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Documento y bandeja de entrada que representan buzones de correo con mailbox en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    mailbox en Python: buzones de correo

    Aprende mailbox en Python para leer, crear y migrar Maildir, mbox y MH con locking, flags, mensajes y manejo seguro

    Ler mais

    Tempo de leitura: 5 minutos
    12/08/2026
    Editor de texto que representa formato con textwrap en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    textwrap en Python: formatea textos

    Aprende textwrap en Python para dividir, rellenar, acortar, indentar y quitar sangrías con control de ancho, espacios y palabras largas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Carpeta y lupa que representan filtros de nombres con fnmatch en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    fnmatch en Python: filtra nombres de archivos

    Aprende fnmatch en Python para filtrar nombres de archivos con comodines, controlar mayúsculas, excluir patrones y distinguir glob de regex.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Monitor con datos binarios que representa arrays numéricos compactos en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    array en Python: números compactos

    Aprende array en Python para almacenar números compactos, usar archivos binarios, byte order, memoryview y buffers seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    10/08/2026
    Círculo cromático que representa conversiones RGB, HSV y HLS con colorsys en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    colorsys en Python: RGB, HSV y HLS

    Aprende colorsys en Python para convertir colores entre RGB, HSV, HLS y YIQ, crear paletas y evitar errores de escala

    Ler mais

    Tempo de leitura: 5 minutos
    09/08/2026
    Icono de configuración que representa archivos plist con plistlib en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    plistlib: lee y escribe archivos plist

    Aprende plistlib en Python para leer y escribir archivos plist XML y binarios, validar datos y manejar fechas, bytes y

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026