Algoritmos de Programación con Python

19.2. Ordenamiento por inserción

Éste otro método de ordenamiento se basa en la siguiente idea:

Paso 0: Partimos de la misma lista de ejemplo utilizada para el ordenamiento por selección.

Lista = | 3 | 2 | -1 | 5 | 0 | 2 |

Paso 1: Considerar el segundo elemento de la lista, y ordenarlo respecto del primero, desplazándolo hasta la posición correcta, si corresponde.

Lista = | 2 | 3 | -1 | 5 | 0 | 2 |

Se desplaza el valor 2 antes de 3.

Paso 2: Considerar el tercer elemento de la lista, y ordenarlo respecto del primero y el segundo, deplazándolo hasta la posición correcta, si corresponde.

Lista = | -1 | 2 | 3 | 5 | 0 | 2 |

Se desplaza el valor —1 antes de 2 y de 3.

Paso 3: Considerar el cuarto elemento de la lista, y ordenarlo respecto del primero, el segundo y el tercero, deplazándolo hasta la posición correcta, si corresponde.

El 5 está correctamente ubicado respecto de —1, 2 y 3 (como el segmento hasta la tercera posición está ordenado, basta con comparar con el tercer elemento del segmento para verificarlo).

Paso N-1:

Todos los elementos excepto el ante-último ya se encuentran ordenados.

Paso N: Considerar el N–ésimo elemento de la lista, y ordenarlo respecto del segmento formado por el primero hasta el N − 1–ésimo, deplazándolo hasta la posición correcta, si corresponde.

Lista = | -1 | 0 | 2 | 2 | 3 | 5 |

Se desplaza el valor 2 antes de 3 y de 5.

Una posible implementación en Python de este algoritmo se incluye en el código 19.2.

La función principal, ord_insercion, recorre la lista desde el segundo elemento hasta el último, y cuando uno de estos elementos no está ordenado con respecto al anterior, llama a la función auxiliar reubicar, que se encarga de colocar el elemento en la posición que le corresponde.

En la función reubicar se busca la posición correcta donde debe colocarse el elemento, a la vez que se van corriendo todos los elementos un lugar a la derecha, de modo que cuando se encuentra la posición, el valor a insertar reemplaza al valor que se encontraba allí anteriormente.

En las siguientes ejecuciones puede verse que funciona correctamente.

>>> l=[3, 2,-1,5, 0, 2]
>>> ord_insercion(l)
DEBUG:   [2,    3,   -1,   5,   0,   2]
DEBUG:   [-1,   2,   3,    5,   0,   2]
DEBUG:   [-1,   2,   3,    5,   0,   2]
DEBUG:   [-1,   0,   2,    3,   5,   2]
DEBUG:   [-1,   0,   2,    2,   3,   5]
>>> print l
[-1, 0, 2, 2, 3, 5]
>>> l=[]
>>> ord_insercion(l)
>>> l=[1]
>>> ord_insercion(l)
>>> print l
[1]
>>> l=[1,2,3,4,5,6]
>>> ord_insercion(l)
DEBUG: [1, 2, 3, 4, 5, 6]
DEBUG: [1, 2, 3, 4, 5, 6]
DEBUG: [1, 2, 3, 4, 5, 6]
DEBUG: [1, 2, 3, 4, 5, 6]
DEBUG: [1, 2, 3, 4, 5, 6]
>>> print l
[1, 2, 3, 4,     5,   6]
# Código 19.2: insercion.py: Ordena una lista por Inserción

#!/usr/bin/env python
#encoding: latin1

def ord_insercion(lista):
    """ Ordena una lista de elementos según el método de inserción.
        Pre: los elementos de la lista deben ser comparables.
        Post: la lista está ordenada. """

    # i va desde la primera hasta la penúltima posición de la lista
    for i in xrange(len(lista)-1):
        # Si el elemento de la posición i+1 está desordenado respecto
        # al de la posición i, reubicarlo dentro del segmento (0:i]
        if lista[i+1]< lista[i]:
            reubicar(lista, i+1)

        print "DEBUG: ", lista

def reubicar(lista, p):
    """ Reubica al elemento que está en la posición p de la lista
        dentro del segmento (0:p-1].
        Pre: p tiene que ser una posicion válida de lista. """

    # v es el valor a reubicar
    v = lista[p]

    # Recorre el segmento (0:p-1] de derecha a izquierda hasta
    # encontrar la posición j tal que lista(j-1] <= v < lista(j].
    j = p
    while j > 0 and v < lista[j-1]:
        # Desplaza los elementos hacia la derecha, dejando lugar
        # para insertar el elemento v donde corresponda.
        lista[j] = lista[j-1]
        # Se mueve un lugar a la izquierda
        j -= 1

    # Ubica el valor v en su nueva posición
    lista[j] = v

19.2.1. Invariante del ordenamiento por inserción

En el ordenamiento por inserción, a cada paso se considera que los elementos que se encuentran en el segmento de 0 a i están ordenados, de manera que agregar un nuevo elemento implica colocarlo en la posición correspondiente y el segmento seguirá ordenado.

19.2.2. ¿Cuánto cuesta ordenar por inserción?

Del código 19.2 se puede ver que la función principal avanza por la lista de izquierda a derecha, mientras que la función reubicar cambia los elementos de lugar de derecha a izquierda.

Lo peor que le puede pasar a un elemento que está en la posición j es que deba ser ubicado al principio de la lista. Y lo peor que le puede pasar a una lista es que todos sus elementos deban ser reubicados.

Por ejemplo, en la lista [10, 8, 6, 2, -2, -5], todos los elementos deben ser reubicados al principio de la lista.

En el primer paso, el segundo elemento se debe intercambiar con el primero; en el segundo paso, el tercer elemento se compara con el segundo y el primer elemento, y se ubica adelante de todo; en el tercer paso, el cuarto elemento se compara con el tercero, el segundo y el primer elemento, y se ubica adelante de todo; etc...

T(N) = c * (2 + 3 + . . . + N ) = c * N * (N + 1)/2 = N^2

Es decir que ordenar por inserción una lista de tamaño N puede insumir (en el peor caso) tiempo del orden de N^2. En cuanto al espacio utilizado, nuevamente sólo se tiene en memoria la lista que se desea ordenar y algunas variables de tamaño 1.

19.2.3. Inserción en una lista ordenada

Sin embargo, algo interesante a notar es que cuando la lista se encuentra ordenada, este algoritmo no hace ningún movimiento de elementos, simplemente compara cada elemento con el anterior, y si es mayor sigue adelante.

Es decir que para el caso de una lista de N elementos que se encuentra ordenada, el tiempo que insume el algoritmo de inserción es:

T(N) = N

Copyright (c) 2011-2014 Rosita Wachenchauzer, Margarita Manterola, Maximiliano Curia, Marcos Medrano, Nicolás Paez. La copia y redistribución de esta página se permite bajo los términos de la licencia Creative Commons Atribución - Compartir Obras Derivadas Igual 3.0 siempre que se conserve esta nota de copyright.