En computación y matemáticas un algoritmo de ordenamiento recursivo es un algoritmo que pone elementos de una lista o un vector en una secuencia dada por una relación de orden, es decir, el resultado de salida ha de ser una permutación —o reordenamiento— de la entrada que satisfaga la relación de orden dada. Las relaciones de orden más usadas son el orden numérico y el orden lexicográfico. Ordenamientos eficientes son importantes para optimizar el uso de otros algoritmos (como los de búsqueda y fusión) que requieren listas ordenadas para una ejecución rápida. También es útil para poner datos en forma canónica y para generar resultados legibles por humanos. Desde los comienzos de la computación, el problema del ordenamiento ha atraído gran cantidad de investigación, tal vez debido a la complejidad de resolverlo eficientemente a pesar de su planteamiento simple y familiar. Por ejemplo, BubbleSort fue analizado desde 1956.1 Aunque muchos puedan considerarlo un problema resuelto, nuevos y útiles algoritmos de ordenamiento se siguen inventado hasta el día de hoy (por ejemplo, el ordenamiento de biblioteca se publicó por primera vez en el 2004). Los algoritmos de ordenamiento son comunes en las clases introductorias a la computación, donde la abundancia de algoritmos para el problema proporciona una gentil introducción a la variedad de conceptos núcleo de los algoritmos, como notación de O mayúscula, algoritmos divide y vencerás, estructuras de datos, análisis de los casos peor, mejor, y promedio, y límites inferiores. Clasificación Los algoritmos de ordenamiento se pueden clasificar de las siguientes maneras: La más común es clasificar según el lugar donde se realice la ordenación o Algoritmos de ordenamiento interno: en la memoria del ordenador. o Algoritmos de ordenamiento externo: en un lugar externo como un disco duro. Por el tiempo que tardan en realizar la ordenación, dadas entradas ya ordenadas o inversamente ordenadas: o Algoritmos de ordenación natural: Tarda lo mínimo posible cuando la entrada está ordenada. o Algoritmos de ordenación no natural: Tarda lo mínimo posible cuando la entrada está inversamente ordenada. Por estabilidad: un ordenamiento estable mantiene el orden relativo que tenían originalmente los elementos con claves iguales. Por ejemplo, si una lista ordenada por fecha se reordena en orden alfabético con un algoritmo estable, todos los elementos cuya clave alfabética sea la misma quedarán en orden de fecha. Otro caso sería cuando no interesan las mayúsculas y minúsculas, pero se quiere que si una clave aBC estaba antes que AbC, en el resultado ambas claves aparezcan juntas y en el orden original: aBC, AbC. Cuando los elementos son indistinguibles (porque cada elemento se ordena por la clave completa) la estabilidad no interesa. Los algoritmos de ordenamiento que no son estables se pueden implementar para que sí lo sean. Una manera de hacer esto es modificar artificialmente la clave de ordenamiento de modo que la posición original en la lista participe del ordenamiento en caso de coincidencia. Los algoritmos se distinguen por las siguientes características: Complejidad computacional (peor caso, caso promedio y mejor caso) en términos de n, el tamaño de la lista o arreglo. Para esto se usa el concepto de orden de una función y se usa la notación O(n). El mejor comportamiento para ordenar (si no se aprovecha la estructura de las claves) es O(n log n). Los algoritmos más simples son cuadráticos, es decir O(n²). Los algoritmos que aprovechan la estructura de las claves de ordenamiento (p. ej. bucket sort) pueden ordenar en O(kn) donde k es el tamaño del espacio de claves. Como dicho tamaño es conocido a priori, se puede decir que estos algoritmos tienen un desempeño lineal, es decir O(n). Uso de memoria y otros recursos computacionales. También se usa la notación O(n). Estabilidad Los algoritmos de ordenamiento estable mantienen un relativo preorden total. Esto significa que un algoritmo es estable solo cuando hay dos registros R y S con la misma clave y con R apareciendo antes que S en la lista original. Cuando elementos iguales (indistinguibles entre sí), como números enteros, o más generalmente, cualquier tipo de dato en donde el elemento entero es la clave, la estabilidad no es un problema. De todas formas, se asume que los siguientes pares de números están por ser ordenados por su primer componente: (4, 1) (3, 7) (3, 1) (5, 6) En este caso, dos resultados diferentes son posibles, uno de los cuales mantiene un orden relativo de registros con claves iguales, y una en la que no: (3, 7) (3, 1) (3, 1) (3, 7) (4, 1) (4, 1) (5, 6) (5, 6) (orden mantenido) (orden cambiado) Los algoritmos de ordenamiento inestable pueden cambiar el orden relativo de registros con claves iguales, pero los algoritmos estables nunca lo hacen. Los algoritmos inestables pueden ser implementados especialmente para ser estables. Una forma de hacerlo es extender artificialmente el cotejamiento de claves, para que las comparaciones entre dos objetos con claves iguales sean decididas usando el orden de las entradas original. Recordar este orden entre dos objetos con claves iguales es una solución poco práctica, ya que generalmente acarrea tener almacenamiento adicional. Ordenar según una clave primaria, secundaria, terciara, etc., puede ser realizado utilizando cualquier método de ordenamiento, tomando todas las claves en consideración (en otras palabras, usando una sola clave compuesta). Si un método de ordenamiento es estable, es posible ordenar múltiples ítems, cada vez con una clave distinta. En este caso, las claves necesitan estar aplicadas en orden de aumentar la prioridad. Ejemplo: ordenar pares de números, usando ambos valores (4, 1) (4, 1) valor) (3, 1) valor) (3, 7) (3, 1) (3, 1) (4, 6) (4, 6) (original) (3, 7) (después de ser ordenado por el segundo (3, 7) (4, 1) (4, 6) (después de ser ordenado por el primer (3, 1) (4, 1) (4, 6) (después de ser ordenado por el primer (4, 1) (4, 6) (3, 7) (después de ser ordenando por el segundo Por otro lado: (3, 7) valor) (3, 1) valor, el orden por el primer valor es perturbado) Lista de algoritmos de ordenamiento Algunos algoritmos de ordenamiento agrupados según estabilidad tomando en cuenta la complejidad computacional. Estables Nombre traducido Nombre original Complejidad Memoria Método Ordenamiento de burbuja Bubblesort O(n²) O(1) Intercambio Ordenamiento de burbuja Cocktail sort bidireccional O(n²) O(1) Intercambio Ordenamiento por inserción Insertion sort O(n²) O(1) Inserción Ordenamiento por casilleros Bucket sort O(n) O(n) No comparativo Ordenamiento por cuentas Counting sort O(n+k) O(n+k) No comparativo Ordenamiento por mezcla Merge sort O(n log n) O(n) Mezcla Ordenamiento con árbol Binary tree sort O(n log n) binario O(n) Inserción Ordenamiento Radix Pigeonhole sort O(n+k) O(k) Radix sort O(n) O(nk) No comparativo Distribution sort O(n³) versión recursiva Gnome sort O(n²) O(n²) Inestables Nombre traducido Nombre original Complejidad Memoria Método Ordenamiento Shell Shell sort O(n1.25) O(1) Inserción Comb sort O(n log n) O(1) Intercambio Ordenamiento por selección Selection sort O(n²) O(1) Selección Ordenamiento por montículos Heapsort O(n log n) O(1) Selección Smoothsort O(n log n) O(1) Selección Quicksort Promedio: O(n log n), O(log n) Partición peor caso: O(n²) Ordenamiento rápido Promedio: O(n u), Several Unique peor caso: O(n²); Sort u=n; u = número único de registros Nombre traducido Cuestionables, imprácticos Nombre Complejidad original Bogosort O(n × n!), peor: no termina O(n), excepto en Pancake sorting máquinas de Von Neumann Randomsort Memoria Método Para todos los algoritmos se cuenta con la siguiente estructura de datos Const MAX = 100 A = arreglo[1..MAX] de enteros Variable N:entero ORDENAMIENTO DIRECTO. MÉTODO DE BURBUJA. Ordena los elementos del arreglo usando el método de burbuja. Transporta en cada pasada el elemento más pequeño a la parte izquierda del arreglo A de N elementos. Burbuja1(A,N) Inicio Declarar i,j,aux:entero Para i 2 hasta N haga Para j i hasta 2 inc (–1) haga Si (A[j-1]>A[j]) entonces Aux A[j-1] A[j-1] A[j] A[j] aux Fin si Fin para Fin para Fin ORDENAMIENTO DIRECTO. METODO DE LA BURBUJA. CONTRARIO AL ANTERIOR. La lógica de este algoritmo cambia con el anterior, en el hecho de que en cada pasada se lleva el valor mas grande a hacia la parte derecha del arreglo A de N elementos. Burbuja2(A,N) Inicio Declarar i,j,aux:entero Para i 1 hasta N-1 haga Para j 1 hasta N-i haga Si (A[j]>A[j+1]) entonces Aux A[j] A[j] A[j+1] A[j+1] aux Fin si Fin para Fin para Fin ORDENAMIENTO DIRECTO CON SEÑAL Algoritmo que usa el principio de ordenamiento del método de la burbuja, usando una señal para saber si en cada pasada hubo intercambios. Arreglo A de N elementos enteros. Burbujaconseñal(A, N) Inicio Declarar i,j,aux:entero Declarar band:booleano i1 Band FALSO Mientras Que (i<= N-1) y (band = FALSO) haga Band VERDADERO Para j 1 hasta N-i haga Si (A[j]>A[j+1]) entonces Aux A[j] A[j] A[j+1] A[j+1] aux Band FALSO Fin si Fin para Fin MQ Fin ORDENAMIENTO POR EL MÉTODO DE LA SACUDIDA (SHAKER SORT) Este método es una optimización del método de la burbuja. Consiste en mezclar las dos formas como se puede realizar el método de ordenamiento directo. En cada pasada hay dos etapas, el la primera etapa se trasladan los elementos más pequeños hacia la izquierda almacenando en una variable el último elemento intercambiado. En la segunda etapa, se trasladan los elementos más grandes hacia la parte derecha del arreglo almacenando en otra variable la posición del último elemento intercambiado. Algoritmo que ordena los elementos del arreglo A, de N elementos, por el método de la sacudida. Shakersort(A,N) Inicio Declarar i, izq, der, k, aux: entero izq 2 der N kN Repetir Para i der hasta izq inc (-1) haga Si (A[i-1]>A[i]) entonces Aux A[i-1] A[i-1] A[i] A[i] aux ki Fin si Fin para Izq k + 1 Para i izq hasta der haga Si (A[i-1] > A[i]) entonces Aux A[i-1] A[i-1] A[i] A[i] Aux ki Fin si Fin para Der k-1 Hasta que izq>der Fin METODO DE ORDENAMIENTO POR INSERCIÓN DIRECTA El objetivo de este método es copiar la forma como los jugadores de cartas ordenan la baraja en una mano. El objetivo de este método es insertar un elemento en la parte izquierda del arreglo que ya se encuentra ordenada. El proceso se repite desde el segundo hasta el n-esimo elemento. Algoritmo que ordena los elementos del arreglo usando el método de inserción directa. Arreglo A de N elementos. insercion(A,N) Inicio Declarar i, aux, k: entero Para i 2 hasta N haga Aux A[i] k i-1 Mientras Que ((k>=1) y (aux<A[k])) haga A[k+1] A[k] k k -1 Fin MQ A[k+1] aux Fin para Fin ORDENAMIENTO POR MÉTODO DE INSERCIÓN BINARIA Es una mejora del método de inserción directa, ya que se hace una búsqueda binaria en lugar de una búsqueda secuencial para insertar el elemento a la izquierda del arreglo, que ya se encuentra ordenado. Y el proceso se repite hasta el n-esimo elemento. Algoritmo que ordena los elementos del arreglo usando el método de inserción binara. Arreglo A de N elementos. insercionbinaria(A,N) Inicio Declarar i, aux, izq, der, m, j: entero Para i 2 hasta N haga Aux A[i] Izq 1 Der i-1 Mientras Que (izq<=der) haga m parteentera((izq+der)/2) Si (aux <= A[m]) entonces Der m-1 Sino Izq m+1 Fin si Fin MQ j i-1 Mientras Que (j>=izq) haga A[j+1] A[j] j j-1 Fin MQ A[izq] aux Fin para Fin ORDENAMIENTO POR SELECCIÓN DIRECTA La idea de este algoritmo es buscar el menor elemento del arreglo y colocarlo en la primera posición, luego se busca el segundo elemento más pequeño del arreglo y se coloca en la segunda posición y así. El algoritmo se basa en: 1. Seleccionar el menor elemento del arreglo. 2. Intercambiar dicho elemento con el primero. 3. Repetir los pasos anteriores con los (n-1), (n-2)... elementos y así sucesivamente hasta que solo quede el elemento mayor. Algoritmo que ordena los elementos de un arreglo usando el método de selección directa. A arreglo de N elementos. seleccion(A,N) Inicio Declarar i, menor, k, j: entero Para i 1 hasta N-1 haga Menor A[i] ki Para j i+1 hasta N haga Si (A[j]<menor) entonces Menor A[j] kj Fin si Fin para A[k] A[i] A[i] menor Fin para Fin COMPARACIÓN DE LOS MÉTODOS DIRECTOS DE ORDENAMIENTO Método item Ordenada Desordenada Orden Inverso Intercambio Directo Inserción Directa Selección Directa comp. Mov 0 comp. Mov 0 comp. Mov ORDENAMIENTO CON EL MÉTODO DE SHELL (INSERCIÓN CON INCREMENTOS DECRECIENTES) Este algoritmo compara cada elemento del arreglo para su ubicación correcta, con los elementos que se encuentran en la parte izquierda del mismo. Este método propone que las comparaciones entre los elementos se efectúen con saltos de mayor tamaño, pero con incrementos decrecientes. Algoritmo que ordena los elementos de un arreglo usando el método de shell. A arreglo de N elementos. shell(A,N) Inicio Declarar int, i, aux: entero Declarar band: booleano Int N+i Mientras Que (int>1) haga int parteentera(int/2) band VERDADERO Mientras Que (band=VERDADERO) haga Band FALSO i1 Mientras Que ((I+int)<=N) haga Si (A[i]>A[i+int]) entonces aux A[i] A[i] A[i+int] A[i+int] aux Band VERDADERO Fin si i i+1 Fin MQ Fin MQ Fin MQ Fin ORDENAMIENTO POR EL MÉTODO DE QUICKSORT (MÉTODO RÁPIDO DE ORDENACIÓN POR PARTICIÓN) Este algoritmo es el mas eficiente y veloz de los métodos de ordenación interna. La idea central del algoritmo es: 1. Se toma un elemento X de una posición cualquiera del arreglo 2. Se trata de ubicar a X en la posición de correcta del arreglo, de tal forma que todos los elementos que se encuentran a su izquierda sean menores o iguales a X y todos los elementos que se encuentran a su derecha sean mayores o iguales a X. 3. Se repiten los pasos anteriores, pero con los conjuntos de datos que se encuentran a la izquierda y a la derecha de la posición correcta de X en el arreglo. 4. El proceso termina cuando todos los elementos se encuentran en su posición correcta en el arreglo. El paso 3 se puede hacer de forma iterativa o recursiva. En este ejemplo, se hará de forma iterativa, dejando el método recursivo para más adelante en el curso. Se necesitan dos algoritmos. Quicksortitera y reduceitera. Algoritmo que ordena los elementos de un arreglo A de N elementos, usando el método Quicksort iterativo. Quicksortitera(A, N) Inicio Declarar top, ini, fin, pos: entero Pilamayor: Arreglo[1..MAX] de entero Pilamenor: Arreglo[1..MAX] de entero Top 1 Pilamenor[top] 1 Pilamayor[top] N Mientras Que (top>0) haga Ini pilamenor[top] Fin pilamayor[top] Top top-1 Pos Reduceitera(ini, fin) Si (ini<(pos-1)) entonces Top top+1 Pilamenor[top] ini Pilamayor[top] pos-1 Fin si Si (fin>(pos+1)) entonces Top top+1 Pilamenor[top] pos+1 Pilamayor[top] fin Fin si Fin MQ Fin Reduceitera(INI, FIN) /* INI y FIN representa las posiciones de los extremos izquierdo y derecho respectivamente del conjunto de elementos a evaluar. En POS es una variable donde se almacenará el resultado de este algoritmo */ Inicio Declarar izq, der, aux, pos: entero Declarar band: booleano Izq ini Der fin Pos ini Band VERDADERO Mientras Que (band=VERDADERO) haga Mientras Que ((A[pos]<=A[der]) y (pos<>der)) haga der der-1 Fin MQ Si (Pos=der) entonces Band FALSO Sino Aux A[pos] A[pos] A[der] A[der] aux Pos der Mientras Que ((A[pos]>=A[izq])y(pos<>izq)) haga Izq izq+1 Fin MQ Si (pos=izq) entonces Band Falso Sino Aux A[pos] A[pos] A[izq] A[izq] aux Pos izq Fin si Fin si Fin MQ Retornar Pos Fin