cv.cm

Programación · Algoritmos

Radix sort (ordenamiento por residuos): cómo funciona, código y complejidad

6 min de lectura

Respuesta corta

Radix sort es un algoritmo de ordenamiento sin comparaciones que ordena enteros o cadenas de longitud fija un dígito (o carácter) cada vez. La versión habitual LSD (dígito menos significativo primero) ordena primero por el último dígito, luego por el siguiente, hasta llegar al más significativo, usando en cada pasada un ordenamiento estable como counting sort. Con d dígitos en base b, se ejecuta en tiempo O(d · (n + b)) y usa O(n + b) de espacio extra. Como cada pasada es estable, el resultado final queda totalmente ordenado y es estable.

Abrir Ordenar líneas

Qué problema resuelve radix sort

Counting sort es lineal, pero necesita un arreglo de conteo tan grande como todo el rango de claves, lo que es imposible con enteros de 32 o 64 bits. Radix sort divide cada clave en dígitos pequeños y ejecuta counting sort una vez por dígito, de modo que cada pasada necesita un arreglo de conteo de solo b entradas (10 para dígitos decimales, 256 para bytes).

Funciona con cualquier clave que se pueda descomponer en una secuencia fija de símbolos pequeños: enteros no negativos, fechas escritas como YYYYMMDD, códigos de longitud fija como los códigos postales, direcciones IP y cadenas de ancho fijo.

Ejemplo resuelto: radix sort LSD en base 10

Ordena [170, 45, 75, 90, 802, 24, 2, 66]. El número mayor tiene 3 dígitos, así que hay 3 pasadas. Cada pasada es un ordenamiento estable por un dígito: los dígitos iguales conservan el orden de la pasada anterior.

PasadaOrdenar porResultado
Inicio[170, 45, 75, 90, 802, 24, 2, 66]
1dígito de las unidades[170, 90, 802, 2, 24, 45, 75, 66]
2dígito de las decenas[802, 2, 24, 45, 66, 170, 75, 90]
3dígito de las centenas[2, 24, 45, 66, 75, 90, 170, 802]

Tras la pasada 2, los números están ordenados por sus dos últimos dígitos. En la pasada 3, 2, 24, 45, 66, 75 y 90 tienen todos el dígito de las centenas 0, y la estabilidad los mantiene en el orden que produjo la pasada 2. Por eso cada pasada debe ser estable.

Implementación en Python

def counting_pass(nums, exp, base):
    """Stable counting sort of nums by the digit at exp."""
    count = [0] * base
    for x in nums:
        count[(x // exp) % base] += 1
    for d in range(1, base):
        count[d] += count[d - 1]
    out = [0] * len(nums)
    for x in reversed(nums):
        d = (x // exp) % base
        count[d] -= 1
        out[count[d]] = x
    return out

def radix_sort(nums, base=10):
    """LSD radix sort for non-negative integers."""
    if not nums:
        return []
    exp, biggest = 1, max(nums)
    while biggest // exp > 0:
        nums = counting_pass(nums, exp, base)
        exp *= base
    return nums

print(radix_sort([170, 45, 75, 90, 802, 24, 2, 66]))
# [2, 24, 45, 66, 75, 90, 170, 802]
print(radix_sort([3, 0, 1, 0], base=256))   # [0, 0, 1, 3]

Usar base 256 significa que cada pasada examina un byte, así que los enteros de 32 bits necesitan solo 4 pasadas. Para incluir números negativos, ordena por separado los valores negativos y los no negativos y únelos, o desplaza todas las claves por el mínimo. Para cadenas de longitud fija, usa el código del carácter en la posición i como dígito y recorre de la última posición a la primera.

Complejidad temporal y espacial

MedidaValorMotivo
Tiempo, en todos los casosO(d · (n + b))d pasadas, cada una un counting sort sobre n claves y b valores de dígito
Espacio extraO(n + b)un búfer de salida y un arreglo de conteo por pasada
Pasadas para claves de 32 bits4 en base 25632 bits ÷ 8 bits por dígito
Comparaciones0los dígitos se usan como índices de un arreglo

Cuando d es una constante (claves de ancho fijo) y b se elige cercano a n o un valor fijo como 256, el tiempo es O(n). Para n claves todas distintas, d debe ser al menos log_b n, así que radix sort no supera a O(n log n) con claves de tamaño arbitrario; es rápido porque cada paso es una operación simple sobre un arreglo.

Cómo se compara radix sort con otros ordenamientos

AlgoritmoMejorPromedioPeorEspacio extraEstable
Radix sort (LSD)O(d · (n + b))O(d · (n + b))O(d · (n + b))O(n + b)Sí
Counting sortO(n + k)O(n + k)O(n + k)O(n + k)Sí
Merge sortO(n log n)O(n log n)O(n log n)O(n)Sí
QuicksortO(n log n)O(n log n)O(n²)O(log n)No
HeapsortO(n log n)O(n log n)O(n log n)O(1)No
Timsort (sorted de Python)O(n)O(n log n)O(n log n)O(n)Sí

En la tabla, n es el número de claves, k el rango de claves de counting sort, d el número de dígitos y b la base. Radix sort LSD es estable siempre que cada pasada por dígito sea estable. Necesita claves con una estructura fija, mientras que los ordenamientos por comparación como merge sort y quicksort funcionan con cualquier orden, incluidas las funciones de comparación personalizadas.

LSD frente a MSD y errores comunes

LSD (dígito menos significativo primero)MSD (dígito más significativo primero)
Orden de las pasadasdel último dígito al primerodel primer dígito al último, de forma recursiva por cubeta
Mejor paraenteros y cadenas de longitud fijacadenas de longitud variable, con salida anticipada en prefijos únicos
Implementaciónbucle simplerecursiva; normalmente cambia a insertion sort en las cubetas pequeñas
Establesí, con una pasada establesí, con una distribución estable en cubetas

Cuándo usar radix sort

Radix sort encaja bien con arreglos grandes de enteros o claves de ancho fijo, como identificadores de 32 bits, marcas de tiempo, direcciones IPv4 o cadenas de longitud fija, sobre todo en GPU y en bibliotecas de alto rendimiento, donde su acceso regular a la memoria compensa. Se usa en la construcción de arreglos de sufijos y en algunas cargas de bases de datos y gráficos. Para listas normales de Python, sorted() casi siempre es más rápido en la práctica porque se ejecuta en código C optimizado. En las entrevistas, radix sort aparece cuando se pregunta cómo ordenar en tiempo lineal, por ejemplo en el problema Maximum Gap.

Preguntas frecuentes

¿Es estable radix sort?

Radix sort LSD es estable cuando cada pasada por dígito usa un ordenamiento estable como counting sort. De hecho, el algoritmo depende de esa estabilidad para ser correcto.

¿Cuál es la complejidad temporal de radix sort?

O(d · (n + b)), donde d es el número de dígitos, n el número de claves y b la base. Con claves de ancho fijo, d y b son constantes, así que es lineal en n.

¿Es radix sort más rápido que quicksort?

Puede serlo con arreglos grandes de enteros de ancho fijo, porque no hace comparaciones y accede a la memoria con un patrón regular. Con datos generales, arreglos pequeños u órdenes personalizados, suele ser mejor quicksort o el ordenamiento integrado.

¿Cuál es la diferencia entre radix sort LSD y MSD?

LSD ordena desde el dígito menos significativo hasta el más significativo con una pasada simple por dígito. MSD empieza por el dígito más significativo y ordena recursivamente cada cubeta, lo que conviene a las cadenas de longitud variable.

¿Puede radix sort ordenar cadenas?

Sí. Trata cada carácter como un dígito. LSD funciona directamente con cadenas de la misma longitud; radix sort MSD maneja cadenas de longitudes distintas.

Todos los tutoriales de programación