Algoritmos de Ordenamiento
Algoritmos de Ordenamiento
Algoritmos de Ordenamiento
Algoritmos de Ordenamiento
Contenido
2.1. Insertion Sort
2.2. Selection Sort
2.3. Shell Sort
2.4. Bubble Sort
2.5. Merge Sort
2.6. Heap Sort
2.7. Partition-Exchange Sort o Quicksort
2.8. Radix Sort
2.9. Address-Calculation Sort
Debido a que las estructuras de datos son utilizadas para almacenar información,
para poder recuperar esa información de manera eficiente es deseable que
aquella esté ordenada. Existen varios métodos para ordenar las diferentes
estructuras de datos básicas.
Los métodos simples son: insertion sort (o por inserción directa) selection sort,
bubble sort, y shellsort, en dónde el último es una extensón al insertion sort,
siendo más rápido. Los métodos más complejos son el quick-sort, el heap sort,
radix y address-calculation sort. 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.
Se ha dicho que el ordenamiento puede efectuarse moviendo los registros con las
claves. El mover un registro completo implica un costo, el cual se incrementa
conforme sea mayor el tamaño del registro. Es por ello que es deseable evitar al
máximo el movimiento de los registros. Una alternativa es el crear una tabla de
referencias a los registros y mover las referencias y no los datos. A continuación
se mostrarán los métodos de ordenamiento empezando por el más sencillo y
avanzando hacia los mas sofisticados
La eficiencia de los algoritmos se mide por el número de comparaciones e
intercambios que tienen que hacer, es decir, se toma n como el número de
elementos que tiene el arreglo a ordenar y se dice que un algoritmo realiza O(n 2)
comparaciones cuando compara n veces los n elementos, n x n = n2.
Una función f(n) se define de orden O(g(n)), es decir, f(n) = O(g(n)) si existen
constantes positivas n0 y c tales que:
Este es uno de los métodos más sencillos. Consta de tomar uno por uno los
elementos de un arreglo y recorrerlo hacia su posición con respecto a los
anteriormente ordenados. Así empieza con el segundo elemento y lo ordena con
respecto al primero. Luego sigue con el tercero y lo coloca en su posición
ordenada con respecto a los dos anteriores, así sucesivamente hasta recorrer
todas las posiciones del arreglo. Este es el algoritmo:
Este procedimiento recibe el arreglo de datos a ordenar a[] y altera las posiciones
de sus elementos hasta dejarlos ordenados de menor a mayor. N representa el
número de elementos que contiene a[].
Ejemplo:
Luego compara esta 's' con lo que hay en la posición j-1, es decir, con 'a'. Debido
a que 's' no es menor que 'a' no sucede nada y avanza i.
Ahora v toma el valor 'o' y lo compara con 's', como es menor recorre a la 's' a la
posición de la 'o'; decrementa j, la cual ahora tiene la posición en dónde estaba la
's'; compara a 'o' con a[j-1] , es decir, con 'a'. Como no es menor que la 'a' sale del
for y pone la 'o' en la posición a[j]. El resultado hasta este punto es el arreglo
siguiente: a = ['a','o','s','r',....]
a = ['a','a','e','e','g','i','l','m','n','o','p','r','s','t','x']
Ejemplo:
2.3. Shellsort
Denominado así por su desarrollador Donald Shell (1959), ordena una estructura
de una manera similar a la del Bubble Sort, sin embargo no ordena elementos
adyacentes sino que utiliza una segmentación entre los datos. Esta segmentación
puede ser de cualquier tamaño de acuerdo a una secuencia de valores que
empiezan con un valor grande (pero menor al tamaño total de la estructura) y van
disminuyendo hasta llegar al '1'. Una secuencia que se ha comprobado ser de las
mejores es: ...1093, 364, 121, 40, 13, 4, 1. En contraste, una secuencia que es
mala porque no produce un ordenamiento muy eficiente es ...64, 32, 16, 8, 4, 2, 1.
Este tipo de ordenamiento es útil cuando se tiene una estructura ordenada y los
nuevos datos a añadir se almacenan en una estructura temporal para después
agregarlos a la estructura original de manera que vuelva a quedar ordenada.
Procedimiento MergeSort
/*recibe el arreglo a ordenar un índice l que indica el límite inferior del arreglo a
ordenar y un índice r que indica el límite superior*/
a = {a,s,o,r,t,i,n,g,e,x,a,m,p,l,e}
{a,s,
o,r,
a,o,r,s,
i,t,
g,n,
g,i,n,t,
a,g,i,n,o,r,s,t,
e,x,
a,m,
a,e,m,x,
l,p,
e,l,p}
a,e,e,l,m,p,x}
a = {a,a,e,e,g,i,l,m,n,o,p,r,s,t,x}
Este método garantiza que el tiempo de ejecución siempre es de: O(n log n)
Procedimiento Heapsort
/* Recibe como parámetros un arreglo a ordenar y un entero que indica el número de datos a
ordenar */
void heapsort(int a[], int N)
{
int k;
for(k=N/2; k>=1; k--)
downheap(a,N,k);
while(N > 1)
{
swap(a,1,N);
downheap(a,--N,1);
}
}
/* el procedimiento downheap ordena el árbol de heap para que el nodo padre sea mayor que sus
hijos */
a = {a,s,o,r,t,i,n}
aiortsn
ainrtso
ainotsr
ainorst
El número con el dígito más grande en la primera posición en la cual los dígitos de
los dos números no coinciden es el mayor de los dos (por supuesto sí coinciden
todos los dígitos de ambos números, son iguales).
Este mismo principio se toma para Radix Sort, para visualizar esto mejor tenemos
el siguiente ejemplo. En el ejemplo anterior se ordeno de izquierda a derecha.
Ahora vamos a ordenar de derecha a izquierda.
Archivo original.
25 57 48 37 12 92 86 33
2 12 92
3 33
5 25
6 86
7 57 37
8 48
10
12 92 33 25 86 57 37 48
1 12
2 25
3 33 37
4 48
5 57
6
8 86
9 92
10
Archivo ordenado: 12 25 33 37 48 57 86 92
ASORTINGEXAMPLE
AEOLMINGEAXTPRS
AEAEGINMLO
AAEEG
AA
AA
EEG
EE
INMLO
LMNO
LM
NO
STPRX
SRPT
PRS
RS
#define NUMELTS 20
void radixsort(int x[], int n)
{
int front[10], rear[10];
struct {
int info;
int next;
} node[NUMELTS];
int exp, first, i, j, k, p, q, y;
int main(void)
{
int x[50] = {NULL}, i;
static int n;
Estado de la lista
i Node[i].info Node[i].next
Inicialización K = 1 K = 2 K =
3
0 65 1 3 1 2
1 789 2 -1 -1 -1
2 123 3 0 3 3
3 457 -1 1 0 1
rear = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
2031
front = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
2031
k=1
p=0p=1p=2p=3
j=5j=9j=3j=7
q = -1 q = -1 q = -1 q = -1
si q == -1 si q == -1 si q == -1 si q == -1
j=3
first = 2
while ( j <= 9)
i=5
si i <= 9
p=5
node[2].next = 0
j=5
i=7
si i <= 9
p=7
node[0].next = 3
j=5
i=9
si i <= 9
p=9
node[2].next = 1
j=9
p=9
node[1].next = -1
Características.
Debido a que el ciclo for (k = 1; k <= m; k++) externo se recorre m veces (una para
cada dígito) y el ciclo interior n veces (una para cada elemento en el archivo) el
ordenamiento es de aproximadamente (m*n).
Si las llaves son complejas (es decir, si casi cada número que puede ser una llave
lo es en realidad) m se aproxima a log n, por lo que (m*n) se aproxima a (n log n).
Ventajas.
Si las máquinas tienen la ventaja de ordenar los dígitos (sobre todo si están en
binario) lo ejecutarían con mucho mayor rapidez de lo que ejecutan una
comparación de dos llaves completas.
25 57 48 37 12 92 86 33
struct{
int info;
int next;
} node[N]
for(i=0;i<n;i++)
node[i].info=x[i];
for(i=0;i<n;i++)
{ /* Insertamos sucesivamente cada elemento en su */
/* subarchivo respectivo utilizando inserción de lista */
y=x[i];
first=y/10; /* Encontrar el dígito de las decenas */
/* Buscar en la lista ligada */
place(&f[first],y);
/* place inserta "y" en su posición correcta en la lista */
/* a la que se apunta mediante f[first] */
}
}
}
}
r();
for(i=0;i<N;i++) /*dato*/
node[i].info=x[i];
printf("\n");
for(i=0;i<N;i++)
printf("x[%d]=%d\n",i,x[i]);
getchar();
}