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

    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 en Python: 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
    Candado digital que representa credenciales por host con netrc en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    netrc en Python: credenciales por host

    Aprende netrc en Python para leer credenciales por host, validar permisos, tratar errores e integrar clientes de red de forma

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026
    Mensaje digital que representa codificación quoted-printable con quopri en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    quopri en Python: quoted-printable

    Aprende quopri en Python para codificar y decodificar quoted-printable en correo, archivos e integraciones MIME de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026
    Icono de archivo digital que representa tipos MIME con mimetypes en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    mimetypes en Python: tipos MIME

    Aprende mimetypes en Python para identificar tipos MIME, extensiones y encodings de forma segura en cargas, descargas, correo y APIs

    Ler mais

    Tempo de leitura: 6 minutos
    08/08/2026
    Código y archivos empaquetados con importlib.resources en Python
    Bibliotecas y Módulos
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources en Python: guía práctica

    Aprende importlib.resources en Python para acceder a archivos empaquetados con seguridad en wheels y aplicaciones instaladas.

    Ler mais

    Tempo de leitura: 5 minutos
    07/08/2026