280 likes | 536 Views
Ordenamiento. Estructuras de datos. Antecedentes. Un archivo de tamaño n es una secuencia de elementos r [0], r [1], …, r [ n -1], a cada elemento del archivo se le llama registro . A cada registro r [ i ] se le asocia una clave o llave k [ i ].
E N D
Ordenamiento Estructuras de datos
Antecedentes Un archivo de tamaño n es una secuencia de elementos r[0], r[1], …, r[n-1], a cada elemento del archivo se le llama registro. A cada registro r[i] se le asocia una clave o llave k[i]. Se dice que el archivo está ordenado de acuerdo a la llave, si i<j implica que k[i] precede a k[j] para algún ordenamiento de las llaves. Un ordenamiento es interno si los registros están en la memoria principal, o externo si se encuentran en almacenamiento auxiliar. Una técnica de ordenamiento es estable si para todos los registros i y j tales que k[i] = k[j], si r[i] precede a r[j] en el archivo original, entonces r[i] precede a r[j] en el archivo ordenado. Un ordenamiento ocurre ya sea sobre los mismos registros o sobre una tabla auxiliar de apuntadores (ordenamiento por dirección).
llaves Otros campos Registro 1 4 DDD 1 AAA Registro 2 2 BBB 2 BBB 1 AAA 3 CCC Registro 3 5 EEE 4 DDD Registro 4 3 CCC 5 EEE Registro 5 Archivo original Archivo ordenado Tabla original de apuntadores Tabla ordenada de apuntadores Registro 1 4 DDD Registro 2 2 BBB 1 AAA Registro 3 Registro 4 5 EEE 3 CCC Registro 5
Eficiencia Para elegir el método de ordenación se deben considerar los siguientes aspectos Tiempo que se debe invertir para codificar el programa La cantidad de tiempo de ejecución El espacio necesario en memoria para ejecutar el programa
Métodos de ordenación Existen tres formas básicas: 1. Ordenación por inserción. 2. Ordenación por selección. 3. Ordenación por intercambio. Estudiaremos una algoritmo simple y otro complejo de cada técnica.
Inserción directa Se selecciona la llave más pequeña y se inserta en el lugar adecuado. 44 55 12 42 94 18 06 67 i=2 44 55 12 42 94 18 06 67 i=3 12 44 55 42 94 18 06 67 i=4 12 42 44 55 94 18 06 67 i=5 12 42 44 55 94 18 06 67 i=6 12 18 42 44 55 94 06 67 i=7 06 12 18 42 44 55 94 67 i=8 06 12 18 42 44 55 67 94
Algoritmo en C void InsercionDirecta(){ int i,j:indice; item x, y; for(i = 1; i<n; i++){ x = a[i]; y = x; j = i-1; while(j>=0&&x<a[j]){ a[j+1] = j>=0?a[j]:y; j = j-1; } a[j+1] = x; } }
Rendimiento El número de comparaciones y movimientos es el siguiente: Cmin = n – 1 Mmin = 2(n – 1) Cmed = (n2 + n – 2)/4 Mmed = (n2 + 9n – 10)/4 Cmax = (n2 + n) – 1 Mmax = (n2 + 3n – 4)
Selección directa Se selecciona el elemento con menor clave y se intercambia con el primero, luego por el segundo, y así sucesivamente. 44 55 12 42 94 18 06 67 06 55 12 42 94 18 44 67 06 12 55 42 94 18 44 67 06 12 18 42 94 55 44 67 06 12 18 42 94 55 44 67 06 12 18 42 44 55 94 67 06 12 18 42 44 55 94 67 06 12 18 42 44 55 67 94
Código en C void SeleccionDirecta(int a[],int n){ int i,j,k; int x; for(i= 0;i<n-1;i++){ k = i; x = a[i]; for(j = i+1;j<n;j++) if(a[j]<x){ k = j; x = a[j]; } a[k] = a[i]; a[i] = x; } }
Rendimiento Número de comparaciones: C = (n*n - n)/2 Número de movimientos: Mmin = 3(n - 1) Mmed = n(ln n + g) Mmax = trunc(n2/4) + 3(n - 1)
Intercambio directo (burbuja) Se intercambian los valores consecutivos del archivo comenzando por el final y se repite hasta haber intercambiado todos los elementos i=2 i=3 i=4 i=5 i=6 i=7 i=8 44 06 06 06 06 06 06 06 55 44 12 12 12 12 12 12 12 55 44 18 18 18 18 18 42 12 55 44 42 42 42 42 94 42 18 55 44 44 44 44 18 94 42 42 55 55 55 55 06 18 94 67 67 67 67 67 67 67 67 94 94 94 94 94
Código en C void Burbuja(int a[],int n){ int i,j,x; for(i = 1;i<n;i++){ for(j = n-1;j>=i;j--) if(a[j-1]>a[j]){ x = a[j-1]; a[j-1] = a[j]; a[j] = x; } } }
La sacudida Una mejora de la burbuja es la sacudida. Consiste en cambiar el orden de recorrido en cada paso de la burbuja. iz=2 3 3 4 4 de=8 8 7 7 4 44 06 06 06 06 55 44 44 12 12 12 55 12 44 18 42 12 42 18 42 94 42 55 42 44 18 94 18 55 55 06 18 67 67 67 67 67 94 94 94
Eficiencia El número de comparaciones es: C = 3/4(n2 – n) El número de movimientos es: Mmin = 0 Mmed = 3(n2 – n)/4 Mmax = 3(n2 – n)/2
Código en C void Sacudida(int a[],int n){ int j,k,iz,de,x; iz = 1; de = n-1; k = n; do{ for(j=de;j>=iz;j--) if(a[j-1]>a[j]){ x = a[j-1]; a[j-1] = a[j]; a[j] = x; k = j; } iz = k+1; for(j = iz; j<=de;j++) if(a[j-1]>a[j]){ x = a[j-1]; a[j-1] = a[j]; a[j] = x; k = j; } de = k-1; }while(iz<=de); }
Ordenamiento de Shell El método es similar al de inserción directa, la variante es que se realiza con intervalos decresientes hasta hacerlo de 1 en 1. 44 55 12 42 94 18 06 67 44 18 06 42 94 55 12 67 06 18 12 42 44 55 94 67 06 12 18 42 44 55 67 94
Eficiencia La eficiencia del algoritmo depende en gran medida de la elección de los incrementos utilizados. Knuth recomienda la secuencia: 1, 4, 13, 40, 121, … donde hk–1= 3hk + 1, ht = 1 y t = log3n – 1 También la secuencia: 1, 3, 7, 15, 31, … donde hk–1= 2hk + 1, ht = 1 y t = log2n – 1 El algoritmo tiene un comportamiento proporcional a n1.2.
Código en C void Shell(int a[],int n){ int incr,i,j,k,span,y,ninc,*inc; ninc = (int)(log(n)/log(3)-1); inc = (int*)malloc(ninc*sizeof(int)); inc[ninc-1] = 1; for(i=ninc-1;i>0;i--) inc[i-1] = 3*inc[i]+1; for(incr=0;incr<ninc;incr++){ span = inc[incr]; for(j = span;j<n;j++){ y = a[j]; for(k = j-span;k>=0 and y<a[k];k -= span) a[k+span] = a[k]; a[k+span] = y; } } }
Ordenamiento del peine El ordenamiento del peine es una mejora respecto al de la burbuja. Se ejecuta la burbuja con incrementos decrecientes. El incremento inicial se sugiere que sea n/1.3, y a cada paso se divide entre 1.3 hasta tener incrementos iguales a 1. A cada paso se ordenan los elementos separados por el incremento reduciendo el total de trabajo a realizar. El último paso se ejecuta con incremento de una unidad asegurando que se ordenen los elementos que no estén en orden todavía,
44 55 12 42 94 18 06 67 06 55 12 42 94 18 44 67 06 18 12 42 94 55 44 67 06 18 12 42 67 55 44 94 06 18 12 42 44 55 67 94 06 12 18 42 44 55 67 94
Código en C void CombSort(int a[],int n){ int gap,i,j,h; bool swap; gap = n; do{ gap = (int)(gap/1.3); if(gap<1) gap = 1; swap = false; for(i = 1;i<n-gap;i++){ j = i+gap; if(a[i]>a[j]){ h = a[i]; a[i] = a[j]; a[j] = h; swap = swap+1; } } }while((swap)and(gap>1)); }
EL rápido El método rápido (quick Sort) se basa en la partición de un arreglo, esta consiste en elegir el elemento de la mitad y colocar todos los elementos más pequeños que él a la izquierda y los más grandes a la derecha. 44 55 12 42 94 06 18 67 18 06 12 42 94 55 44 67 El proceso se repite para la parte izquierda y derecha de la partición, y re repite recursivamente hasta tener el arreglo ordenado.
Código en C void rapido(int a[],int iz, int de){ int i,j,x,w; i = iz; j = de; x = a[(iz+de)/2]; do{ while(a[i]<x) i++; while(x<a[j]) j--; if(i<=j){ w = a[i]; a[i] = a[j]; a[j] = w; i++; j--; } }while(i<=j); if(iz<j) rapido(a,iz,j); if(i<de) rapido(a,i,de); }
Eficiencia El rápido tiene un comportamiento proporcional a n log n. Desgraciadamente en el peor de los casos pude tener un comportamiento proporcional a n2.