- Conceptos
preliminares - Cuándo aplicar un
método - Tipos de
ordenamientos - Clasificación de los
algoritmos de ordenamiento - Algoritmo
burbuja - Algoritmo
inserción - Algoritmo
selección - Algoritmo
Shake - Algoritmo
Shell - Ejercicios
de aplicación - Bibliografía
El ordenamiento es una labor común que realizamos
cotidianamente, es un proceso tan
común en nuestras vidas que no nos detenemos a meditar
mucho en ello. Ordenar es meramente colocar información de una manera especial
basándonos en un criterio de ordenamiento.
En la ciencia de
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. El propósito principal de un ordenamiento es
el de facilitar la búsqueda de
información.
El ordenar un grupo de datos
significa mover los datos o sus referencias para que queden en
una secuencia tal que represente un orden, el cual puede ser
numérico, alfabético o incluso alfanumérico,
ascendente o descendente.
Antes de comenzar a ver cada algoritmo
vamos a ponernos de acuerdo en algunos conceptos, para que no
haya confusiones:
- Clave: La parte de un registro por la
cual se ordena la lista. Por ejemplo, una lista de registros con
campos nombre, dirección y teléfono se puede ordenar
alfabéticamente de acuerdo al nombre. En este caso los
campos dirección y teléfono no se toman en cuenta
en el ordenamiento. - Criterio de ordenamiento o de
comparación: El criterio que utilizamos para asignar
orden a los registros con base en una o más claves. De
esta manera decidimos si un registro es mayor o menor que
otro. - Registro: Un grupo de datos que forman la
lista. Pueden ser datos primitivos enteros, caracteres, reales,
etc., o grupos de
ellos, que en C++ equivalen a estructuras
de datos.
Cuando se requiere hacer una cantidad considerable de
búsquedas y es importante el factor tiempo.
Al estudiar algoritmos de
todo tipo, no sólo de ordenamiento, es bueno tener una
forma de evaluarlos antes de pasarlos a código.
De esta manera podremos decidir cuál se adapta mejor a los
requerimientos de nuestro programa.
Así que veamos estos aspectos:
- Estabilidad: Cómo se comporta con
registros que tienen claves iguales. Algunos algoritmos
mantienen el orden relativo entre éstos y otros no.
Veamos un ejemplo. Si tenemos la siguiente lista de
datos:
(Nombre, Edad)
Pedro 19, Juan 23, Felipe 15, Marcela 20, Juan 18,
Marcela 17
Ordenada alfabéticamente por el nombre con un
algoritmo estable quedaría así:
Felipe 15, Marcela 20, Marcela 17, Juan 23, Juan
18, Pedro 19
Un algoritmo no estable podría dejar a Juan
18 antes de Juan 23, o a Marcela 20
después de Marcela 17.
- Tiempo de ejecución: La complejidad del
algoritmo, que no tiene que ver con dificultad del algoritmo,
mas bien con el rendimiento o la eficiencia del
algoritmo. Es una función
independiente de la implementación o de los factores
externos. Tendremos que identificar una operación
fundamental que realice nuestro algoritmo, que en este caso es
comparar. Ahora contamos cuántas veces el algoritmo
necesita comparar. Si en una lista de n términos realiza
n comparaciones la complejidad es O(n). Una medida de
eficiencia es:
- Contar el número de comparaciones
- Contar el número de movimientos de
ítems - Estos están en función del
número de elementos a ser ordenados. - Un buen algoritmo de ordenamiento requiere de O(n log
n) comparaciones.
- Requerimientos de memoria: El
algoritmo puede necesitar memoria adicional para realizar su
labor. En general es preferible que no sea así, pero es
común en la programación tener que sacrificar memoria
por rendimiento.
Los datos a ordenar están en memoria la principal
RAM, por lo que
se asume que el tiempo que se requiere para acceder cualquier
elemento sea el mismo.
Inserción Directa | Inserción Directa |
Inserción Binaria | |
Selección Directa | Selección Directa |
Intercambio Directo | Burbuja |
Shake | |
Inserción Disminución | Shell |
Ordenamiento De Árbol | Heap |
Tournament | |
Sort Particionado | Quick Sort |
Merge Sort | |
Radix Sort | |
Cálculo De Dirección |
Los datos a ordenar están en la memoria
secundaria, es decir, disco duro,
disco extraíble, por lo que se asume que el tiempo que se
requiere para acceder a cualquier elemento depende de la
última posición consultada.
Straight Merging |
Natural Merging |
Balanced Multiway Merging |
Polyphase Sort |
Distribution Of Initial Runs |
Clasificación de
los algoritmos de ordenamiento
Algoritmos de inserción: | Inserción Binaria Hashing |
Algoritmos de intercambio: | |
Algoritmos de selección: | |
Algoritmos de enumeración: |
Los métodos
simples son: Inserción Directa, Selección, Burbuja
y Shell, en dónde el último es una extensión
al método de
Inserción, siendo más rápido. Los
métodos más complejos son Quick Sort y
Heap.
Bubble Sort recorre el arreglo intercambiando los
elementos adyacentes que estén desordenados. Recorre el
arreglo tantas veces hasta que ya no haya cambios.
Prácticamente lo que hace es tomar el elemento mayor y lo
coloca en las últimas posiciones o tomar el menor y
colocarlo en las primeras posiciones.
ALGORITMO BURBUJA
INICIO
ENTERO X, Z, ARREGLO[N]
X ß 0
MIENTRAS(X < N)
{
Z ß N
MIENTRAS(Z >= 0)
{
SI(ARREGLO[Z] < ARREGLO[Z-1])
{
INTERCAMBIO(ARREGLO[Z],ARREGLO[Z-1])
}
Z ß Z –
1
}
X ß X + 1
}
FIN
void burbuja(void)
{
int x, z, aux, ARREGLO[N];
x = 0;
while(x < N)
{
z = N;
while(z >= 0)
{
if(ARREGLO[z] < ARREGLO[z – 1])
{
aux = ARREGLO[z];
ARREGLO[z] = ARREGLO[z – 1];
ARREGLO[z – 1] = aux;
}
z–;
}
x++;
}
}
Tiempo de ejecución: El ciclo interno se
ejecuta n veces. El ciclo externo también se ejecuta n
veces, la complejidad es n * n = O(n2). El comportamiento
del caso promedio depende del orden de entrada de los datos, pero
es sólo un poco mejor que el del peor caso, y sigue siendo
O(n2).
Estabilidad: No intercambia registros con claves
iguales.
Ventajas:
- Fácil implementación.
- No requiere memoria adicional.
Desventajas:
- Muy lento.
- Muchas comparaciones.
- Muchos intercambios.
Este método consiste en insertar un elemento en
una parte ya ordenada del vector y comenzar de nuevo con los
elementos restantes. Este también es un algoritmo lento,
pero puede ser de utilidad para
listas semiordenadas, pues en ese caso realiza pocos
desplazamientos.
ALGORITMO INSERCIÓN
INICIO
ENTERO X, Z, AUX, ARREGLO[N]
LOGICO B
PARA(X ß 1, HASTA N,
X ß X + 1)
{
AUX ß
ARRAY[X]
Z ß X – 1
B ß FALSO
MIENTRAS(B = FALSO Y Z >= 0)
{
SI(AUX < ARREGLO[Z])
{
ARREGLO[Z + 1] ß
ARREGLO[Z]
Z ß Z –
1
}
SI NO
{
B ß
VERDAD
}
}
ARREGLO[Z + 1] ß
AUX
}
FIN
void insercion(void)
{
int x,z, aux, ARREGLO[N];
bool b;
for(x = 1; x < N; x++)
{
aux = ARREGLO[x];
z = x – 1;
flag = false;
while(b == false && z >= 0)
{
if(aux < ARREGLO[z])
{
ARREGLO[z + 1] = ARREGLO[z];
z–;
}
else
b = true;
}
ARREGLO[z + 1] = aux;
}
}
Ejemplo:
[0] | 15 | 15 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 7 |
[1] | 10 | ð 15 | 15 | 15 | 15 | 14 | 14 | 14 | 14 | 11 | 11 | 11 | 11 | ð 10 | 10 |
[2] | 14 | 14 | 14 | ð 15 | 15 | 15 | 15 | ð 14 | 14 | 14 | 14 | 14 | ð 11 | 11 | 11 |
[3] | 11 | 11 | 11 | 11 | 11 | 11 | ð 15 | 15 | 15 | 15 | 15 | ð 14 | 14 | 14 | 14 |
[4] | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | ð 15 | 15 | 15 | 15 | 15 |
ê | ê | ê | ê | ê | |||||||||||
X | 1 | 2 | 3 | 4 | 5 | ||||||||||
Z | 0 | -1 | 1 | 0 | 2 | 1 | 0 | 3 | 2 | 1 | 0 | -1 | 4 | ||
B | F | F | T | F | T | F | F | ||||||||
AUX | 10 | 14 | 11 | 7 | ? |
Tiempo de Ejecución: Para una
lista de n elementos el ciclo externo se ejecuta n-1 veces. El
ciclo interno se ejecuta como máximo 1, 2, 3 veces a la
tercera iteración, etc., esto tratándose del pero
caso posible. Esto produce una complejidad
O(n2).
Estabilidad: No intercambia
registros con claves iguales. Por lo tanto es estable.
Ventajas:
- Fácil implementación.
- Requerimientos mínimos de memoria.
Desventajas:
- Lento.
- Numerosas comparaciones.
Página siguiente |