Metodos de Ordenamientos

Anuncio
UAA – Sistemas Electrónicos
3
Estructura de Datos
Serna
Métodos de Ordenamiento
3.1
Tipos de Ordenamiento
La ordenación o clasificación de datos consiste en la disposición de los mismos de acuerdo con
algún valor o característica. Por ejemplo, cada elemento de una agenda telefónica tiene un campo
nombre, un campo dirección y un campo número telefónico. Por lo regular los datos en la agenda
se encuentran organizados en un orden de la A la Z. De la misma forma un lista ó vector de datos
se dice que esta ordenado de manera ascendente, si X [ i ] <= X [ i +1] y, por otro lado, se dice
que esta ordenado de manera descendente sí X [ i ] >= X [ i +1].
El proceso de ordenación es uno de los mecanismos más interesantes cuando llega el momento
de mostrar que existen múltiples soluciones para un mismo problema, y que cada solución
algorítmica tiene sus propias ventajas y desventajas.
Una forma de medir la eficiencia de un algoritmo de esta clase, es verificar el número de
comparaciones entre valores clave, además del número de movimientos que se tengan que
realizar entre elementos (intercambios) de la lista.
Los métodos de ordenamiento que trabajan con estructuras de datos residentes en memoria
principal se denominan Ordenamientos Internos, mientras que las implementaciones que utilizan
estructuras de datos residentes en archivos se conocen como Ordenamientos externos.
3.2
Ordenamiento Interno
Los métodos de ordenamiento interno trabajan en memoria principal y sus implementaciones son
muy variadas, de manera que la elección del algoritmo adecuado debe realizarse con criterios de
eficiencia (tiempo y ejecución) y en función de la memoria disponible. Dividiremos los métodos en
dos grandes grupos:
•
•
Directos (burbuja, selección e inserción).
Logarítmicos (Shell sort, Merge sort, Heap sort, Quick sort, Radix).
En el caso de listas pequeñas, los métodos directos se desempeñan de manera relativamente
eficientes, ya que la codificación del algoritmo correspondiente no es compleja. Su uso es muy
frecuente. Sin embargo, en arreglos grandes las ordenaciones directas resultan ineficientes y se
necesitara un método logarítmico para su solución.
3.2.1 Ordenación por intercambio (burbuja)
Es uno de los métodos relativamente más sencillo e intuitivo, pero también resulta ser muy
ineficiente. Se basa en la ordenación por cambio, y recibe su nombre de la semejanza con las
burbujas de un depósito de agua donde cada burbuja busca su propio nivel.
Los pasos a efectuar en el caso de una ordenación ascendente (en el caso de la ordenación
descenderte solo habría que cambiar el signo de comparación) son:
1. Comparar el primer y segundo elemento, intercambiarlos si el primero es mayor que el
segundo; luego se compara el primero con el tercero, intercambiándose en caso necesario, y el
proceso se repite hasta llegar al último elemento. De este modo, tras la primera iteración la
casilla primera conservara el elemento más pequeño de esa iteración.
1
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
2. Se repite el paso anterior, pero ahora con el segundo y tercero, en caso de ser necesario se
intercambian, y así hasta llegar a comparar el segundo con el ultimo.
Consideremos el siguiente ejemplo. Se cuenta con un vector de 6 posiciones donde se inicia una
lista de números { 7, 2, 8, 3, 5, 1 }, la cual será ordenada en forma ascendente { 1, 2, 3, 5, 7, 8 },
observe como se declara una variable constante entera llamada n la cual tiene un valor de 6,
enseguida se declara un vector de tipo entero que contendrá una cantidad n de casillas, en este
caso 6, declaramos también las variables i y j que nos ayudaran a desplazarnos entre casilla y
casilla para hacer las comparaciones. Y finalmente la variable tem, almacenara temporalmente el
valor a intercambiar entre las casillas que lo necesiten.
El proceso sería de la siguiente manera:
1ra iteración, i permanece fijo en la casilla 0 y j se incrementa hasta llegar al último elemento:
{ 7, 2, 8, 3, 5, 1 } j = 1, intercambio generando { 2, 7, 8, 3, 5, 1 }, enseguida
{ 2, 7, 8, 3, 5, 1 } j = 2, no genera intercambio
{ 2, 7, 8, 3, 5, 1 } j = 3, no genera intercambio
{ 2, 7, 8, 3, 5, 1 } j = 4, no genera intercambio
{ 2, 7, 8, 3, 5, 1 } j = 5, intercambio generando { 1, 7, 8, 3, 5, 2 }, termina iteración
2da iteración, i permanece fijo en la casilla 1 y j se incrementa hasta llegar al último elemento:
{ 1, 7, 8, 3, 5, 2 } j = 2, no genera intercambio
{ 1, 7, 8, 3, 5, 2 } j = 3, intercambio generando { 1, 3, 8, 7, 5, 2 }, enseguida
{ 1, 3, 8, 7, 5, 2 } j = 4, no genera intercambio
{ 1, 3, 8, 7, 5, 2 } j = 5, intercambio generando { 1, 2, 8, 7, 5, 3 }, termina iteración
3ra iteración, i permanece fijo en la casilla 2 y j se incrementa hasta llegar al último elemento:
{ 1, 2, 8, 7, 5, 3 } j = 3, intercambio generando { 1, 2, 7, 8, 5, 3 }
{ 1, 2, 7, 8, 5, 3 } j = 4, intercambio generando { 1, 2, 5, 8, 7, 3 }
{ 1, 2, 5, 8, 7, 3 } j = 5, intercambio generando { 1, 2, 3, 8, 7, 5 }, termina iteración
4ta iteración, i permanece fijo en la casilla 3 y j se incrementa hasta llegar al último elemento:
{ 1, 2, 3, 8, 7, 5 } j = 4, intercambio generando { 1, 2, 3, 7, 8, 5 }
{ 1, 2, 3, 7, 8, 5 } j = 5, intercambio generando { 1, 2, 3, 5, 8, 7 }, termina iteración
5ta iteración, i permanece fijo en la casilla 4 y j se incrementa hasta llegar al último elemento:
{ 1, 2, 3, 5, 8, 7 } j = 5, intercambio generando { 1, 2, 3, 5, 7, 8 }, termina proceso
void burbuja(int * v, int n) /*** código en C++ ***/
{
/* ordenamiento de burbuja */
for (int i = 0; i < n-1; i++)
for (int j = i + 1; j < n; j++) {
if ( v[i] > v[j] ) { //realiza la comparación
int tem = v[i]; //si es cierta intercambia los datos
v[i] = v[j];
v[j] = tem;
}
}
}
Algoritmo de Burbuja mejorado
Existe una versión mejorada de dicho algoritmo en donde se integra una variable que funge como
switch (bandera) que permite detectar el momento en que ya no se presenten mas intercambios
aunque su mejora no suele ser tan importante pues el algoritmo sigue comportándose como una
2
ordenación cuadrática O(n ).
2
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
void burbuja(int *v, int n) /*** código en C++ ***/
{
bool sw = true;
int li = 0;
do {
li++;
sw = true;
for (int i = 0; i < n - li; i++) {
if ( v[i] > v[i+1] ) { // compara los valores
// intercambia los datos
int tem = v[i];
v[i] = v[i+1];
v[i+1] = tem;
sw = false;
}
}
} while(!sw);
}
3.2.2 Método de selección
La idea básica es encontrar el elemento más pequeño (grande), en orden ascendente de la lista, e
intercambiarlo con el elemento que ocupa la primera posición en la lista, a continuación se busca el
siguiente elemento más pequeño y se transfiere a la segunda posición. Se repite el proceso hasta
que el último elemento ha sido transferido a su posición correcta.
El algoritmo de ordenación depende a su vez del algoritmo necesario para localizar el componente
mayor (menor) de un array. Es un proceso muy similar al método de la burbuja pero haciendo más
eficiente la búsqueda y evitando intercambios innecesarios.
Consideremos el mismo arreglo del ejemplo anterior { 7, 2, 8, 3, 5, 1 }. El proceso sería de la
siguiente manera:
1ra iteración, i permanece fijo en la casilla 0 y j se incrementa hasta llegar al último elemento:
{ 7, 2, 8, 3, 5, 1 } k = 0, j = 1, cambio k = j ya que en j esta el menor
{ 7, 2, 8, 3, 5, 1 } k = 1, j = 2, no genera cambio
{ 7, 2, 8, 3, 5, 1 } k = 1, j = 3, no genera cambio
{ 7, 2, 8, 3, 5, 1 } k = 1, j = 4, no genera cambio
{ 7, 2, 8, 3, 5, 1 } k = 1, j = 5, cambio k = j ya que en j esta el menor, genera { 1, 2, 8, 3, 5, 7 }
2da iteración, i permanece fijo en la casilla 1 y j se incrementa hasta llegar al último elemento:
{ 1, 2, 8, 3, 5, 7 } k = 1, j = 2 - 5, no genera cambios
3ra iteración, i permanece fijo en la casilla 2 y j se incrementa hasta llegar al último elemento:
{ 1, 2, 8, 3, 5, 7 } k = 2, j = 3, cambio k = j ya que en j esta el menor
{ 1, 2, 8, 3, 5, 7 } k = 3, j = 4, no genera cambio
{ 1, 2, 8, 3, 5, 7 } k = 3, j = 5, no genera cambio, termina el ciclo, genera { 1, 2, 3, 8, 5, 7 }
4ta iteración, i permanece fijo en la casilla 3 y j se incrementa hasta llegar al último elemento:
{ 1, 2, 3, 8, 5, 7 } k = 3, j = 4, cambio k = j ya que en j esta el menor
{ 1, 2, 3, 8, 5, 7 } k = 4, j = 5, no genera cambio, termina el ciclo, genera { 1, 2, 3, 5, 8, 7 }
5ta iteración, i permanece fijo en la casilla 4 y j se incrementa hasta llegar al último elemento:
{ 1, 2, 3, 5, 8, 7 } k = 4, j = 5, cambio k = j ya que en j esta el menor, genera { 1, 2, 3, 5, 7, 8 }
3
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
void seleccion( int * v, int n) /*** código en C++ ***/
{
/* ordenamiento de seleccion */
for (int i = 0, j = 0, k = 0; i < n-1; i++) {
k = i;
for (j = i+1; j < n; j++)
if ( v[k] > v[j] )
k = j;
int tem = v[i];
v[i] = v[k];
v[k] = tem;
}
}
3.2.3 Método de Inserción
Este método también se denomina “método del jugador de cartas”, por la semejanza con la forma
de clasificar las cartas de una baraja, insertando cada carta en el lugar adecuado.
El algoritmo ordena los dos primeros elementos de la lista, a continuación el tercer elemento se
inserta en la posición que corresponda, el cuarto se inserta en la lista de tres elementos, y así
sucesivamente. Este proceso continua hasta que la lista este totalmente ordenada.
Sea una lista A[1], A[2], ... A[n]. Los pasos a dar para una ordenación ascendente son:
1. Ordenar A[1] y A[2].
2. Comparar A[3] con A[2], si A[3] es mayor o igual a que A[2], sigue con el siguiente elemento si
no se compara A[3] con A[1]; si A[3] es mayor o igual que A[1], insertar A[3] entre A[1] yA[2].
Si A[3] es menor que A[1], entonces transferir A[3] a A[1], A[1] a A[2] y A[2] a A[3].
3. Se suponen ordenados los n-1 primeros elementos y corresponde insertar el n-ésimo
elemento. Si A[m] es mayor que A[k] (con K = 1, 2, ..., m-1), se debe correr una posición
A[k+1], ... A[m-1] y almacenar A[m] en la posición k+1.
Consideremos el mismo arreglo del ejemplo anterior { 7, 2, 8, 3, 5, 1 }. El proceso sería de la
siguiente manera:
1ra iteración, i permanece fijo en la casilla 1 y j se decrementa mientras el elemento es menor a j:
{ 7, 2, 8, 3, 5, 1 } tem = 2, j = 0, mientras j >= 0 y tem < 7, { 7, 7, 8, 3, 5, 1 }, j se decrementa en 1
{ 7, 7, 8, 3, 5, 1 } tem = 2, j = -1, mientras j >=0, rompe mientras, tem ingresa { 2, 7, 8, 3, 5, 1 }
2da iteración, i permanece fijo en la casilla 2 y j se decrementa mientras el elemento es menor a j:
{ 2, 7, 8, 3, 5, 1 } tem = 8, j = 1, mientras j >= 0 y tem < 7, rompe mientras { 2, 7, 8, 3, 5, 1 }
3ra iteración, i permanece fijo en la casilla 3 y j se decrementa mientras el elemento es menor a j:
{ 2, 7, 8, 3, 5, 1 } tem = 3, j = 2, mientras j >= 0 y tem < 8, { 2, 7, 8, 8, 5, 1 }, j se decrementa en 1
{ 2, 7, 8, 8, 5, 1 } tem = 3, j = 1, mientras j >= 0 y tem < 7, { 2, 7, 7, 8, 5, 1 }, j se decrementa en 1
{ 2, 7, 7, 8, 5, 1 } tem = 3, j = 0, mientras j >= 0 y tem < 2, rompe, tem ingresa { 2, 3, 7, 8, 5, 1 }
4ta iteración, i permanece fijo en la casilla 4 y j se decrementa mientras el elemento es menor a j:
{ 2, 3, 7, 8, 5, 1 } tem = 5, j = 3, mientras j >= 0 y tem < 8, { 2, 3, 7, 8, 8, 1 }, j se decrementa en 1
{ 2, 3, 7, 8, 8, 1 } tem = 5, j = 2, mientras j >= 0 y tem < 7, { 2, 3, 7, 7, 8, 1 }, j se decrementa en 1
{ 2, 3, 7, 7, 8, 1 } tem = 5, j = 1, mientras j >= 0 y tem < 3, rompe, tem ingresa { 2, 3, 5, 7, 8, 1 }
5ta iteración, i permanece fijo en la casilla 5 y j se decrementa mientras el elemento es menor a j:
{ 2, 3, 5, 7, 8, 1 } tem = 1, j = 4, mientras j >= 0 y tem < 8, { 2, 3, 5, 7, 8, 8 }, j se decrementa en 1
{ 2, 3, 5, 7, 8, 8 } tem = 1, j = 3, mientras j >= 0 y tem < 7, { 2, 3, 5, 7, 7, 8 }, j se decrementa en 1
4
UAA – Sistemas Electrónicos
{ 2, 3, 5, 7, 7, 8 } tem
{ 2, 3, 5, 5, 7, 8 } tem
{ 2, 3, 3, 5, 7, 8 } tem
{ 2, 2, 3, 5, 7, 8 } tem
Estructura de Datos
Serna
= 1, j = 2, mientras j >= 0 y tem < 5, { 2, 3, 5, 5, 7, 8 }, j se decrementa en 1
= 1, j = 1, mientras j >= 0 y tem < 3, { 2, 3, 3, 5, 7, 8 }, j se decrementa en 1
= 1, j = 0, mientras j >= 0 y tem < 2, { 2, 2, 3, 5, 7, 8 }, j se decrementa en 1
= 1, j = -1, mientras j >= 0, rompe mientras, tem ingresa { 1, 2, 3, 5, 7, 8 }
void insercion( int * v, int n) /*** código en C++ ***/
{
/* ordenamiento de insercion */
for (int i = 1, j = 0; i < n; i++) {
int tem = v[i];
j = i - 1;
while ( j >= 0 && tem < v[j]) {
v[j+1] = v[j];
j--;
}
v[j+1] = tem;
}
}
3.2.4
Ordenación Shell
Shell sort lleva este nombre en honor a su inventor, Donald Shell, que lo publicó en 1959. La idea
básica de este método es distribuir el arreglo de manera que se genere una matriz de valores
donde cada elemento es comparado de manera adyacente empleando un mecanismo de inserción
directa simple, dicho rango que genera grupos de manera matricial que es reducido gradualmente
hasta estabilizarse en un valor uniforme de 1.
En el método de ordenación por inserción directa es empleado en cada sub grupo de manera que
cada elemento se compara para su ubicación correcta en el arreglo con los elementos que se
encuentran en su parte izquierda. Si el elemento a insertar es más pequeño que el grupo de
elementos que se encuentran a su izquierda, será necesario efectuar varias comparaciones antes
de su ubicación. Shell propone que las comparaciones entre elementos se efectúen con saltos de
mayor tamaño, pero con incrementos decrecientes; así, los elementos quedaran ordenados más
rápidamente.
El algoritmo para Shell sort seria el siguiente. Algunos autores suponen una secuencia geométrica
de decremento (2.2) que permite una distribución presumiblemente más razonable para el
acomodo de los grupos de elementos a comparar. El algoritmo emplea un arreglo a de 0 a n-1:
inc = round(n/2)
mientras inc > 0 {
para i = inc hasta n – 1 {
temp = a[i]
j = i
mientras j = inc && a[j - inc] > temp {
a[j] = a[j - inc]
j = j – inc
}
a[j] = temp
}
inc = round(inc / 2.2)
}
5
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
Consideremos el siguiente ejemplo, tenemos una lista de números como 13 14 94 33 82 25 59 94
65 23 45 27 73 25 39 10. Si comenzamos con un tamaño de paso de 5, podríamos visualizar esto
dividiendo la lista de números en una tabla con 5 columnas. Esto quedaría así:
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
25 59 94 65 82
45
Entonces ordenamos cada columna, lo que nos da
10 14 73 25 23
13 27 94 33 39
Cuando lo leemos de nuevo como una única lista de números, obtenemos 10 14 73 25 23 13 27 94
33 39 25 59 94 65 82 45. Aquí, el 10 que estaba en el extremo final, se ha movido hasta el extremo
inicial. Esta lista es entonces de nuevo ordenada usando un ordenamiento con un espacio de 3
posiciones, y después un ordenamiento con un espacio de 1 posición (ordenamiento por inserción
simple).
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
25 23 33
27 25 59
39 65 73
45 94 82
94
Generando …
10 14 13
Y finalmente …
10 14 13 25 23 33 27 25 59 39 65 73 45 94 82 94
10 13 14 23 25 25 27 33 39 45 59 65 73 82 94 94
void shell_sort(int A[], int size) /*** código en C++ ***/
{
int j, temp;
int incrmnt = size/2;
while (incrmnt > 0) {
for (int i = incrmnt; i < size; i++) {
j = i;
temp = A[i];
while ((j >= incrmnt) && (A[j-incrmnt] > temp)) {
A[j] = A[j - incrmnt];
j = j - incrmnt;
}
A[j] = temp;
}
incrmnt /= 2;
}
}
3.2.5
Ordenación por Mezcla (Merge Sort)
Fue desarrollado en 1945 por John Von Neumann. Conceptualmente, el ordenamiento por mezcla
funciona de la siguiente manera:
1. Si la longitud de la lista es 0 ó 1, entonces ya está ordenada. En otro caso:
2. Dividir la lista desordenada en dos sublistas de aproximadamente la mitad del tamaño.
6
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
3. Ordenar cada sublista recursivamente aplicando el ordenamiento por mezcla.
4. Mezclar las dos sublistas en una sola lista ordenada.
El ordenamiento por mezcla incorpora dos ideas principales para mejorar su tiempo de ejecución:
Una lista pequeña necesitará menos pasos para ordenarse que una lista grande.
Se necesitan menos pasos para construir una lista ordenada a partir de dos listas también
ordenadas, que a partir de dos listas desordenadas. Por ejemplo, sólo será necesario
entrelazar cada lista una vez que están ordenadas.
A continuación se describe el algoritmo en pseudocódigo (se advierte de que no se incluyen casos
especiales para vectores vacíos, una implementación en un lenguaje de programación real debería
tener en cuenta estos detalles):
funcion merge_sort( list m )
if length(m) ≤ 1
return m
var list left, right, result
var integer middle = length(m) / 2
for each x in m up to middle
add x to left
for each x in m after middle
add x to right
left = merge_sort(left)
right = merge_sort(right)
result = merge(left, right)
return result
funcion merge( list left, right)
var list result
while length(left) > 0 or length(right) > 0
if length(left) > 0 and length(right) > 0
if first(left) ≤ first(right)
append first(left) to result
left = rest(left)
else
append first(right) to result
right = rest(right)
else if length(left) > 0
append first(left) to result
left = rest(left)
else if length(right) > 0
append first(right) to result
right = rest(right)
end while
return result
Considere el siguiente ejemplo con una lista { 5, 2, 4, 6, 1, 3, 2, 6 }, la lista es dividida en la primera
de las dos llamadas a merge con { 5, 2, 4, 6 }, después nuevamente es dividida en { 5, 2 }, se
intercala obteniendo { 2, 5 }, la segunda llamada recupera { 4, 6 } y se intercalan para obtener { 2,
4, 5, 6 }. Con la mitad { 1, 3, 2, 6 } ocurre la misma reducción de elementos tras las llamadas
intercalando finalmente la salida para obtener { 1, 2, 2, 3, 4, 5, 6, 6 }.
7
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
La siguiente Figura1 describe mejor el proceso de retro-seguimiento (backtrack) que construye la
lista ordenada una vez que la recursividad alcanza su caso base.
Figura 1
void intercala(int *a, int na, int *b, int nb, int *v)/* código en C++ */
{
int xa=0, xb=0, xv=0;
while (xa < na && xb < nb) {
if (a[xa] < b[xb])
v[xv] = a[xa++];
else
v[xv] = b[xb++];
xv++;
}
while (xa < na)
v[xv++] = a[xa++];
while (xb < nb)
v[xv++] = b[xb++];
}
void merge(int v[], int n) {
int *a,*b, na,nb, x,y;
if ( n > 1 ) {
if (n%2 == 0)
na = nb = (int) n / 2;
else {
na = (int) n / 2;
nb = na + 1;
}
a = new int [na];
b = new int [nb];
for(x = 0; x < na; x++)
a[x] = v[x];
for(y = 0; y < nb; x++,y++)
b[y] = v[x];
merge(a, na);
8
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
merge(b, nb);
intercala(a, na, b, nb, v);
delete a, b;
}
}
3.2.6
Ordenación por partición e intercambio (Quick Sort)
Es un algoritmo relativamente eficiente y representa una mejora sustancial al método de
intercambio directo.
El algoritmo es el siguiente:
1. Elegir un elemento de la lista de elementos a ordenar (pivote).
2. Resituar los demás elementos de la lista a cada lado del pivote, de manera que a un lado
queden todos los menores que él, y al otro los mayores. Los elementos iguales al pivote
pueden ser colocados tanto a su derecha como a su izquierda, dependiendo de la
implementación deseada. En este momento, el pivote ocupa exactamente el lugar que le
corresponderá en la lista ordenada.
3. La lista queda separada en dos sub-listas, una formada por los elementos a la izquierda del
pivote, y otra por los elementos a su derecha.
4. Repetir este proceso de forma recursiva para cada sub-lista mientras éstas contengan más
de un elemento. Una vez terminado este proceso todos los elementos estarán ordenados.
Como se puede suponer, la eficiencia del algoritmo depende de la posición en la que termine el
pivote elegido. En el mejor caso, el pivote termina en el centro de la lista, dividiéndola en dos sublistas de igual tamaño. En este caso, el orden de complejidad del algoritmo es O(n log n).
En el peor caso, el pivote termina en un extremo de la lista. El orden de complejidad del algoritmo
es entonces de O(n²). El peor caso dependerá de la implementación del algoritmo, aunque
habitualmente ocurre en listas que se encuentran ordenadas, o casi ordenadas. Pero
principalmente depende del pivote, por ejemplo el algoritmo implementado toma como pivote
siempre el primer elemento del arreglo, y el arreglo que le pasamos está ordenado, siempre va a
generar a su izquierda un arreglo vacío, lo que es ineficiente.
En el siguiente ejemplo se marcan el pivote y los índices i y j con las letras p, i y j respectivamente.
Comenzamos con la lista completa. El elemento pivote será el 4:
5 - 3 - 7 - 6 - 2 - 1 - 4
p
Comparamos con el 5 por la izquierda y el 1 por la derecha.
5 - 3 - 7 - 6 - 2 - 1 - 4
i
j
p
5 es mayor que 4 y 1 es menor. Intercambiamos:
1 - 3 - 7 - 6 - 2 - 5 - 4
i
j
p
9
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
Avanzamos por la izquierda y la derecha:
1 - 3 - 7 - 6 - 2 - 5 - 4
i
j
p
3 es menor que 4: avanzamos por la izquierda. 2 es menor que 4: nos mantenemos ahí.
1 - 3 - 7 - 6 - 2 - 5 - 4
i
j
p
7 es mayor que 4 y 2 es menor: intercambiamos.
1 - 3 - 2 - 6 - 7 - 5 - 4
i
j
p
Avanzamos por ambos lados:
1 - 3 - 2 - 6 - 7 - 5 - 4
iyj
p
En este momento termina el ciclo principal, porque los índices se cruzaron. Ahora intercambiamos
lista[i] con lista[sup].
1 - 3 - 2 - 4 - 7 - 5 - 6
p
Aplicamos recursivamente a la sub-lista de la izquierda (índices 0 - 2). Tenemos lo siguiente:
1 - 3 - 2
1 es menor que 2: avanzamos por la izquierda. 3 es mayor: avanzamos por la derecha. Como se
intercambiaron los índices termina el ciclo. Se intercambia lista[i] con lista[sup]:
1 - 2 - 3
El mismo procedimiento se aplicará a la otra sub-lista. Al finalizar y unir todas las sub-listas queda
la lista inicial ordenada en forma ascendente.
1 - 2 - 3 - 4 - 5 - 6 - 7
int partions(int l[],int low,int high)
{
int prvotkey=l[low];
while (low<high)
{
while (low<high && l[high]>=prvotkey)
--high;
swap(l,high,low);
while (low<high && l[low]<=prvotkey)
++low;
int dummy;
dummy=l[low];
l[low]=l[high];
l[high]=dummy;
}
10
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
}
return low;
}
void qsort(int l[],int low,int high)
{
int prvotloc;
if(low<high)
{
prvotloc=partions(l,low,high);
qsort(l,low,prvotloc);
qsort(l,prvotloc+1,high);
}
}
void quicksort(int l[],int n)
{
qsort(l,0,n);
}
3.2.7
Ordenación basado en comparaciones (Heap Sort)
Es una variante del algoritmo de selección, El ordenamiento por montículos (Heap sort) es un
algoritmo de ordenación no recursivo, no estable, con complejidad computacional O(n log n).
Este algoritmo consiste en almacenar todos los elementos del vector a ordenar en un montículo
(heap), y luego extraer el nodo que queda como nodo raíz del montículo (cima) en sucesivas
iteraciones obteniendo el conjunto ordenado. Basa su funcionamiento en una propiedad de los
montículos, por la cual, la cima contiene siempre el menor elemento (o el mayor, según se haya
definido el montículo) de todos los almacenados en él.
El significado de heap en computación es el de una cola de prioridades (priority queue). Tiene las
siguientes características:
Un heap es un arreglo de n posiciones ocupado por los elementos de la cola.
Se mapea un árbol binario de tal manera en el arreglo que el nodo en la posición i es el
padre de los nodos en las posiciones (2*i) y (2*i+1).
El valor en un nodo es mayor o igual a los valores de sus hijos. Por consiguiente, el nodo
padre tiene el mayor valor de todo su subárbol
heap
arreglo
11
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
Heap Sort consiste esencialmente en:
•
•
•
3.3
convertir el arreglo en un heap
construir un arreglo ordenado de atrás hacia adelante (mayor a menor) repitiendo los
siguientes pasos:
o sacar el valor máximo en el heap (el de la posición 1)
o poner ese valor en el arreglo ordenado
o reconstruir el heap con un elemento menos
utilizar el mismo arreglo para el heap y el arreglo ordenado.
Ordenamiento Externo
La ordenación de archivos se lleva a cabo cuando el volumen de los datos a tratar es demasiado
grande y los mismos no caben en la memoria principal de la computadora.
Al ocurrir esta situación no pueden aplicarse los métodos de ordenación interna, de modo que
debe pensarse en otro tipo de algoritmos para ordenar datos almacenados en archivos.
Por ordenación de archivos se entiende, entonces, la ordenación o clasificación de éstos,
ascendente o descendentemente, de acuerdo con un campo determinado al que se denominará
campo clave. La principal desventaja de esta ordenación es el tiempo de ejecución, debido a las
sucesivas operaciones de entrada y salida.
Los dos métodos de ordenación externa más importantes son los basados en la mezcla directa y
en la mezcla equilibrada.
3.3.1
Ordenación por mezcla directa
El método de ordenación por mezcla directa es probablemente el más utilizado por su fácil
comprensión.
La idea central de este algoritmo consiste en la realización sucesiva de una partición y una fusión
que produce secuencias ordenadas de longitud cada vez mayor. En la primera pasada la
participación es de longitud 1 y la fusión o mezcla produce secuencias ordenadas de longitud 4.
Este proceso se repite hasta que la longitud de la secuencia para la partición sea mayor o igual
que la longitud de la secuencia para la partición sea mayor o igual que el número de elementos del
archivo original.
Supóngase que se desean ordenar las claves del archivo F. Para realizar tal actividad se utilizan
dos archivos auxiliares a los que se les denominará F1 y F2.
F: 09 75 14 68 29 17 31 25 04 05 13 18 72 46 61
PRIMERA PASADA
Partición en secuencias de longitud 1.
F1: 09 14 29 31 04 13 72 61
12
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
F2: 75 68 17 25 05 18 46
Fusión en secuencias de longitud 2.
F1: 09 75 14 68 17 29 25 31 04 05 13 18 46 72 61
SEGUNDA PASADA
Partición en secuencias de longitud 2.
F1: 09 75 17 29 04 05 46 72
F2: 14 68 25 31 13 18 61
Fusión en secuencias de longitud 4.
F1: 09 14 68 75 17 25 29 31 04 05 13 18 46 61 72
TERCERA PASADA
Partición en secuencias de longitud 4.
F1: 09 14 68 75 04 05 13 18
F2: 17 25 29 31 46 61 72
Fusión en secuencias de longitud 8.
F1: 09 14 75 25 29 31 68 75 04 05 13 18 46 61 72
CUARTA PASADA
Partición en secuencias de longitud 8.
F1: 09 14 17 25 29 31 68 75
F2: 04 05 13 18 46 61 72
Fusión en secuencias de longitud 16.
F1: 04 05 09 13 14 17 18 25 29 31 46 61 68 72 75
3.3.2
Ordenación por mezcla equilibrada
El método de ordenación por mezcla equilibrada, conocido también con el nombre de mezcla
natural, es una optimización del método de mezcla directa.
La idea central de este método consiste en realizar las particiones tomando secuencias ordenadas
de máxima longitud en lugar de secuencias de tamaño fijo previamente determinadas. Luego
realiza la fusión de las secuencias ordenadas, alternativamente sobre dos archivos aplicando estas
acciones en forma repetida se lograra que el archivo original quede ordenado. Para la realización
de este proceso de ordenación se necesitaran cuatro archivos. El archivo original F y tres archivos
auxiliares a los que se denominaran F1, F2 y F3. De estos archivos, dos serán considerados de
entrada y dos de salida; esto, alternativamente, con el objeto de realizar la fusión-partición. El
proceso termina cuando en la realización de una fusión-partición el segundo archivo quede vacío.
13
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
EJEMPLO :
Suponga que se desean ordenar las claves del archivo F utilizando el método de mezcla
equilibrada.
F: 09 75 14 68 29 17 31 25 04 05 13 18 72 46 61
Los pasos que se realizan son los siguientes:
PARTICIÓN INICIAL
F2: 09 75 29 25 46 61
F3: 14 68 17 31 04 05 13 18 72
PRIMERA FUSION-PARTICION
F: 09 14 68 75 04 05 13 18 25 46 61 72
F1: 17 29 31
SEGUNDA FUSION-PARTICION
F2: 09 14 17 29 31 68 75
F3: 04 05 13 18 25 46 61 72
TERCERA FUSION-PARTICION
F: 04 05 09 13 14 17 18 25 29 31 46 61 68 72 75
F1:
Obsérvese que al realizar la tercera fusión-partición el segundo archivo queda vació, por lo que se
puede afirmar que el archivo ya se encuentra ordenado.
14
UAA – Sistemas Electrónicos
Estructura de Datos
Serna
Bibliografía
Y. Langsam, M. J. Augenstein, A. Tenenbaum. Data Structures using C and C++. Prentice Hall,
Second edition. ISBN 0-13-036997-7.
http://en.wikipedia.org/wiki/Sorting_algorithm
15
Descargar