Algoritmos de Ordenamiento
Algoritmos de Ordenamiento
Algoritmos de Ordenamiento
1. Introducción.
El ordenamiento es una labor común que realizamos
continuamente. ¿Pero te has preguntado qué es ordenar? ¿No? Es
que es algo tan corriente en nuestras vidas que no nos detenemos a
pensar en ello. Ordenar es simplemente colocar información de una
manera especial basándonos en un criterio de ordenamiento.
En la computación el ordenamiento de datos también cumple un
rol muy importante, ya sea como un fin en sí o como parte de otros
procedimientos más complejos. Se han desarrollado muchas
técnicas en este ámbito, cada una con características específicas, y
con ventajas y desventajas sobre las demás. Aquí voy a mostrarte
algunas de las más comunes, tratando de hacerlo de una manera
sencilla y comprensible.
2. Conceptos Preliminares.
Antes de comenzar a ver cada algoritmo vamos a ponernos de
acuerdo en algunos conceptos, para que no haya confusiones:
7. Bibliografía.
H.M. Deitel, P.J. Deitel: "Cómo programar en C/C++". Editorial
Prentice Hall.
Charles Bowman: "Algoritmos y estructuras de datos:
Aproximación en C". Oxford University Press, 1999.
"Dictionary of Algorithms, Data Structures, and Problems"
1 Ordenamiento Burbuja
(Bubblesort)
1. Descripción.
Este es el algoritmo más sencillo probablemente. Ideal para
empezar. Consiste en ciclar repetidamente a través de la lista,
comparando elementos adyacentes de dos en dos. Si un elemento
es mayor que el que está en la siguiente posición se intercambian.
¿Sencillo no?
2. Pseudocódigo en C.
Tabla de variables
Nombre Tipo Uso
lista Cualquiera Lista a ordenar
TAM Constante entera Tamaño de la lista
i Entero Contador
j Entero Contador
El mismo que los elementos de Para realizar los
temp
la lista intercambios
3. Un ejemplo
Vamos a ver un ejemplo. Esta es nuestra lista:
4-3-5-2-1
3-4-5-2-1
3-4-2-5-1
3-4-2-1-5
3-2-1-4-5
2-1-3-4-5
1-2-3-4-5
4. Optimizando.
Se pueden realizar algunos cambios en este algoritmo que
pueden mejorar su rendimiento.
Si observas bien, te darás cuenta que en cada pasada a
través de la lista un elemento va quedando en su posición final.
Si no te queda claro mira el ejemplo de arriba. En la primera
pasada el 5 (elemento mayor) quedó en la última posición, en la
segunda el 4 (el segundo mayor elemento) quedó en la
penúltima posición. Podemos evitar hacer comparaciones
innecesarias si disminuimos el número de éstas en cada
pasada. Tan sólo hay que cambiar el ciclo interno de esta
manera:
for (j=0; j<TAM - i; j++)
Puede ser que los datos queden ordenados antes de completar
el ciclo externo. Podemos modificar el algoritmo para que
verifique si se han realizado intercambios. Si no se han hecho
entonces terminamos con la ejecución, pues eso significa que
los datos ya están ordenados. Te dejo como tarea que
modifiques el algoritmo para hacer esto :-).
Otra forma es ir guardando la última posición en que se hizo un
intercambio, y en la siguiente pasada sólo comparar hasta
antes de esa posición.
Ventajas:
Fácil implementación.
No requiere memoria adicional.
Desventajas:
Muy lento.
Realiza numerosas comparaciones.
Realiza numerosos intercambios.
2. Pseudocódigo en C.
Tabla de variables
Nombre Tipo Uso
lista Cualquiera Lista a ordenar
TAM Constante entera Tamaño de la lista
i Entero Contador
Posición del menor elemento
pos_men Entero
de la lista
El mismo que los elementos Para realizar los
temp
de la lista intercambios
3. Un ejemplo.
Vamos a ordenar la siguiente lista (la misma del ejemplo anterior
:-) ):
4-3-5-2-1
1-3-5-2-4
1-2-5-3-4
1-2-3-5-4
1-2-3-4-5
Ventajas:
Fácil implementación.
No requiere memoria adicional.
Realiza pocos intercambios.
Rendimiento constante: poca diferencia entre el peor y el mejor
caso.
Desventajas:
Lento.
Realiza numerosas comparaciones.
Este es un algoritmo lento. No obstante, ya que sólo realiza un
intercambio en cada ejecución del ciclo externo, puede ser una
buena opción para listas con registros grandes y claves pequeñas.
Si miras el programa de demostración notarás que es el más rápido
en la parte gráfica (por lo menos en un PC lento y con una tarjeta
gráfica mala como el mío x-|). La razón es que es mucho más lento
dibujar las barras que comparar sus largos (el desplazamiento es
más costoso que la comparación), por lo que en este caso especial
puede vencer a algoritmos como Quicksort.
Bien, ya terminamos con éste. Otra vez te recomiendo que
hagas un programa y trates de implementar este algoritmo, de
preferencia sin mirar el código ni el pseudocódigo otra vez.
3 Ordenamiento por Inserción
1. Descripción.
Este algoritmo también es bastante sencillo. ¿Has jugado
cartas?. ¿Cómo las vas ordenando cuando las recibes? Yo lo hago
de esta manera: tomo la primera y la coloco en mi mano. Luego
tomo la segunda y la comparo con la que tengo: si es mayor, la
pongo a la derecha, y si es menor a la izquierda (también me fijo en
el color, pero omitiré esa parte para concentrarme en la idea
principal). Después tomo la tercera y la comparo con las que tengo
en la mano, desplazándola hasta que quede en su posición final.
Continúo haciendo esto, insertando cada carta en la posición que le
corresponde, hasta que las tengo todas en orden. ¿Lo haces así tu
también? Bueno, pues si es así entonces comprenderás fácilmente
este algoritmo, porque es el mismo concepto.
Para simular esto en un programa necesitamos tener en cuenta
algo: no podemos desplazar los elementos así como así o se
perderá un elemento. Lo que hacemos es guardar una copia del
elemento actual (que sería como la carta que tomamos) y desplazar
todos los elementos mayores hacia la derecha. Luego copiamos el
elemento guardado en la posición del último elemento que se
desplazó.
2. Pseudocódigo en C.
Tabla de variables
Nombre Tipo Uso
lista Cualquiera Lista a ordenar
TAM Constante Entera Tamaño de la lista
Nombre Tipo Uso
i Entero Contador
j Entero Contador
El mismo que los elementos de Para realizar los
temp
la lista intercambios
3. Un ejemplo
¿Te acuerdas de nuestra famosa lista?
4-3-5-2-1
4-4-5-2-1
3-4-5-2-1
Ventajas:
Fácil implementación.
Requerimientos mínimos de memoria.
Desventajas:
Lento.
Realiza numerosas comparaciones.
2. Pseudocódigo en C.
Tabla de variables
Nombre Tipo Uso
lista Cualquiera Lista a ordenar
inf Entero Elemento inferior de la lista
sup Entero Elemento superior de la lista
El mismo que los
elem_div El elemento divisor
elementos de la lista
El mismo que los
temp Para realizar los intercambios
elementos de la lista
i Entero Contador por la izquierda
j Entero Contador por la derecha
El ciclo continua mientras cont
cont Entero
tenga el valor 1
Nombre Procedimiento: OrdRap
Parámetros:
lista a ordenar (lista)
índice inferior (inf)
índice superior (sup)
// Inicialización de variables
1. elem_div = lista[sup];
2. i = inf - 1;
3. j = sup;
4. cont = 1;
// Clasificamos la sublista
7. while (cont)
8. while (lista[++i] < elem_div);
9. while (lista[--j] > elem_div);
10. if (i < j)
11. temp = lista[i];
12. lista[i] = lista[j];
13. lista[j] = temp;
14. else
15. cont = 0;
// Aplicamos el procedimiento
// recursivamente a cada sublista
19. OrdRap (lista, inf, i - 1);
20. OrdRap (lista, i + 1, sup);
3. Un ejemplo
Esta vez voy a cambiar de lista ;-D
5-3-7-6-2-1-4
5-3-7-6-2-1-4
5-3-7-6-2-1-4
1-3-7-6-2-5-4
1-3-7-6-2-5-4
1-3-7-6-2-5-4
1-3-2-6-7-5-4
1-3-2-6-7-5-4
1-3-2-4-7-5-6
Aplicamos recursivamente a la sublista de la izquierda (índices 0
- 2). Tenemos lo siguiente:
1-3-2
1-2-3
7-5-6
5-7-6
5-6-7
1-2-3-4-5-6-7
4. Optimizando.
Sólo voy a mencionar algunas optimizaciones que pueden
mejorar bastante el rendimiento de quicksort:
Hacer una versión iterativa: Para ello se utiliza una pila en que
se van guardando los límites superior e inferior de cada
sublista.
No clasificar todas las sublistas: Cuando el largo de las
sublistas va disminuyendo, el proceso se va encareciendo. Para
solucionarlo sólo se clasifican las listas que tengan un largo
menor que n. Al terminar la clasificación se llama a otro
algoritmo de ordenamiento que termine la labor. El indicado es
uno que se comporte bien con listas casi ordenadas, como el
ordenamiento por inserción por ejemplo. La elección de n
depende de varios factores, pero un valor entre 10 y 25 es
adecuado.
Elección del elemento de división: Se elige desde un conjunto
de tres elementos: lista[inferior], lista[mitad] y lista[superior]. El
elemento elegido es el que tenga el valor medio según el
criterio de comparación. Esto evita el comportamiento
degenerado cuando la lista está prácticamente ordenada.
f(1) = 1
f(n) = n + 2 f(n/2)
La forma cerrada de esta expresión es:
f(n) = n log2n
Ventajas:
Muy rápido
No requiere memoria adicional.
Desventajas: