Algoritmos de Ordenamiento por Inserción: Inserción Directa y Shell Sort

Introducción al Ordenamiento

El ordenamiento es una operación fundamental en la informática que consiste en reorgenizar un conjunto de datos según un criterio específico, generalmente en orden ascendente o descendente basado en una clave. La eficiencia de estos algoritmos es crucial para optimizar el rendimiento de aplicaciones que procesan y buscan información en grandes volúmenes de datos.

Ordenamiento por Inserción Directa

El algoritmo de inserción directa construye la secuencia ordenada de manera incremental. Su lógica se basa en tomar cada elemento de la colección no ordenada e insertarlo en la posición correcta dentro de la subcolección que ya ha sido procesada y ordenada previamente.

El proceso para un ordenamiento ascendente funciona de la siguiente manera:

  • Se considera el primer elemento como una sublista ordenada de un solo elemento.
  • Para cada elemento subsiguiente, se compara con los elementos de la sublista ordenada, desplazándose de derecha a izquierda.
  • Los elementos mayores que el valor actual se desplazan una posición a la derecha para hacer espacio.
  • El valor actual se inserta en la posición vacante encontrada.

Implementación en C:

void insertionSort(int* arr, int size) {
    for (int i = 1; i < size; i++) {
        int key = arr[i];
        int j = i - 1;

        // Desplazar elementos mayores que 'key' hacia la derecha
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

Características de la Inserción Directa

  • Eficiencia: Su rendimiento es óptimo cuando la colección está casi ordenada, reduciendo drásticamente el número de comparaciones y desplazamientos.
  • Complejidad Temporal: $O(N^2)$ en el peor caso (orden inverso) y caso promedio. En el mejor caso (arreglo ya ordenado), la complejidad es $O(N)$.
  • Complejidad Espacial: $O(1)$, ya que opera in-place sin requerir memoria adicional significativa.
  • Estabilidad: Es un algoritmo estable, lo que significa que mantiene el orden relativo original de los elementos con claves iguales.

Ordenamiento Shell (Shell Sort)

El Shell Sort es una generalización del ordenamiento por inserción que mejora su eficiencia al permitir el intercambio de elementos que están separados por una distancia considerable. Utiliza una secuencia de incrementos (conocida como gap) para dividir virtualmente la lista en subgrupos intercalados.

El flujo del algoritmo es el siguiente:

  1. Se define un valor inicial para el incremento gap.
  2. La lista se divide conceptualmente en subgrupos de elementos separados por la distancia gap.
  3. Se aplica un ordenamiento por inserción dentro de cada subgrupo.
  4. Se reduce el valor de gap y se repite el proceso de ordenamiento.
  5. Cuando gap llega a 1, se realiza una inserción directa final sobre una lista que ya está parcialmente ordenada, resultando en un proceso de inserción final mucho más rápido.

Implementación en C utilizando la secuencia de incrementos clásica (división por 2):

void shellSort(int* dataset, int length) {
    // Inicializar el gap con la mitad del tamaño del arreglo
    for (int gap = length / 2; gap > 0; gap /= 2) {
        
        // Realizar un insertion sort adaptado al gap actual
        for (int i = gap; i < length; i++) {
            int temp = dataset[i];
            int j;
            
            // Desplazar elementos hasta encontrar la ubicación correcta para 'temp'
            for (j = i; j >= gap && dataset[j - gap] > temp; j -= gap) {
                dataset[j] = dataset[j - gap];
            }
            dataset[j] = temp;
        }
    }
}

Características del Shell Sort

  • Optimización: Mitiga el principal cuello de botella de la inserción directa, que es el desplazamiento de elementos posición por posición, permitiendo saltos grandes al principio.
  • Fase de Pre-ordenamiento: Mientras gap > 1, el algoritmo realiza pasadas que acercan rápidamente los elementos a su posición final, creando un arreglo "casi ordenado".
  • Complejidad Temporal: Depende estrictamente de la secuencia de incrementos utilizada. Con la secuencia clásica de Shell (división por 2), la complejidad en el peor caso sigue siendo $O(N^2)$. Sin embargo, con secuencias matemáticamente más avanzadas (como las de Hibbard, Knuth o Sedgewick), el rendimiento mejora drásticamente, alcanzando complejidades cercanas a $O(N^{1.3})$ o $O(N \log^2 N)$.
  • Estabilidad: Debido a los saltos e intercambios entre elementos distantes, el Shell Sort no es un algoritmo estable.

Etiquetas: estructuras-de-datos algoritmos-de-ordenamiento insertion-sort shell-sort complejidad-algoritmica

Publicado el 7-12 04:27