TEMA 2. RECURSIÓN

Anuncio
1.1 TIPOS Y FUNCIONES AUXILIARES
//@ pre: (0<=i) && (i<a.length) && (0<=j) && (j<a.length)
static void intercambia (int[] a, int i, int j) {
int temp;
temp = a[i];
a[i] =a [j];
a[j] = temp;
}
//@ pre: (0<=prim) && (prim<=ult) && (ult<a.length)
static int posMinimo (int[] a, int prim, int ult) {
int k;
int pmin = prim;
for ( k = prim+1; k <= ult; k++ )
if ( a[k] < a[pmin] )
pmin = k;
return pmin;
}
//@ pre: (0<=prim) && (prim<=ult) && (ult<a.length)
static int posMaximo (int[] a, int prim, int ult) {
int k;
int pmax = prim;
for ( k = prim+1; k <= ult; k++ )
if ( a[k] > a[pmax] )
pmax = k;
return pmax;
}
1
1.2 ORDENACIÓN POR INSERCIÓN
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length)
public static void insercion (int[] a, int prim, int ult) {
int p, temp, j;
for ( p = prim+1; p <= ult; p++ ) {
temp = a[p];
j = p-1;
while ( ( prim <= j ) && ( temp < a[j] ) ) {
a[j+1] = a[j];
j--;
}
a[j+1] = temp;
}
}
1.3 ORDENACIÓN POR SELECCIÓN
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length)
public static void seleccion (int[] a, int prim, int ult) {
int i;
for ( i = prim; i < ult; i++ ) {
intercambia (a, i, posMinimo (a, i, ult));
}
}
1.4 ORDENACIÓN POR BURBUJA
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length)
public static void burbuja (int[] a, int prim, int ult) {
int i, j;
for ( i = prim; i<ult; i++)
for (j = ult; j > i; j--)
if ( a[j-1] > a[j] )
intercambia(a,j-1,j);
}
2
1.5 ORDENACIÓN POR MEZCLA
Este método utiliza la técnica de divide y vencerás para realizar la ordenación del vector
a. Así divide a a en dos subvectores, que son ordenados por llamadas recursivas a este
método, y luego mezcla los dos subvectores ya ordenados:
//@ pre: (0<=prim) && (prim<=ult) && (ult<a.length) && (a.length = b.length)
public static void mezcla (int [] a,int [] b, int prim, int ult) {
int mitad;
if ( prim < ult ) {
mitad = (prim + ult) / 2;
mezcla (a, b, prim, mitad);
mezcla (a, b, mitad+1, ult);
combinar (a, b, prim, mitad, mitad+1, ult);
}
}
//@ pre: (0<=p1) && (p1<=u1) && (u1=p2-1) && (p2<=u2) && (u2<a.length)
//
&& (a.length= b.length)
static void combinar (int[] a, int[] b, int p1, int u1, int p2, int u2) {
int k, i1, i2;
for ( k = p1; k <= u2; k++ ) b[k] = a[k]; // copiamos a en b
i1 = p1;
i2 = p2;
for ( k = p1; k <= u2; k++ ) {
if ( b[i1] <= b[i2] ) {
a[k] = b[i1];
if ( i1 < u1 ) i1++;
else b[i1] = Integer.MAX_VALUE;
}
else {
a[k] = b[i2];
if ( i2 < u2 ) i2++;
else b[i2] = Integer.MAX_VALUE;
}
}
}
Complejidad
Este método ordena n elementos en tiempo Θ(nlogn) en cualquiera de los casos peor,
mejor o medio. Sin embargo tiene una complejidad espacial (en cuanto a memoria) mayor
que los demás (del orden de n).
3
1.6 ORDENACIÓN HEAPSORT
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length)
public static void monticulos (int [] a, int prim, int ult) {
int i;
hacerMonticulo (a, prim, ult);
for ( i = ult; i >= prim; i-- ) {
intercambia (a, prim, i);
empujar (a, prim, i-1, prim);
}
}
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length)
// construye un heap a partir de a[prim..ult]. Complejidad O(n)
static void hacerMonticulo (int [] a, int prim, int ult) {
int cal = ( ult - prim+1 ) / 2;
int i;
for ( i = cal; i >= 0; i-- )
empujar (a, prim, ult, prim+i);
}
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length) && (prim<=i<=ult)
// empuja el elemento de a en posicion i, hasta su posicion final en un heap
static void empujar (int [] a, int prim, int ult, int i) {
int k = i - prim;
int j = k;
if( ( 2*j+1 <= ult-prim ) && ( a[2*j+1+prim] > a[k+prim] ) )
k = 2*j+1;
if( ( 2*j+1 < ult-prim ) && ( a[2*j+2+prim] > a[k+prim] ) )
k = 2*j+2;
intercambia (a, j+prim, k+prim );
while ( k != j ) {
j = k;
if ( ( 2*j+1 <= ult-prim ) && ( a[2*j+1+prim] > a[k+prim] ) )
k = 2*j+1;
if ( ( 2*j+1 < ult-prim ) && ( a[2*j+2+prim] > a[k+prim] ) )
k = 2*j+2;
intercambia (a, j+prim, k+prim);
}
}
Complejidad
La complejidad temporal de este método es Θ(nlogn) independientemente de la entrada
(del caso), y no usa memoria extra. Es unas dos veces más lento que el método Quicksort
en el caso medio.
4
1.7 ORDENACIÓN QUICKSORT
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length)
public static void quicksort (int [] a, int prim, int ult) {
int l;
if ( prim < ult ) {
l = pivote (a, a[prim], prim, ult );
quicksort (a, prim, l-1);
quicksort (a, l+1, ult);
}
}
La función Pivote parte de un elemento especial (llamado pivote) y permuta los
elementos del vector de forma que al finalizar la función, todos los elementos menores o
iguales que el pivote estén a su izquierda, y los elementos mayores que él a su derecha.
Devuelve la posición en la que ha quedado situado el pivote p:
//@ pre: (0<=prim) && (prim<=ult) && (ult<a.length)
static int pivote (int [] a, int p, int prim, int ult) {
int i = prim+1;
int l = ult;
while ( (i <= ult) && ( a[i] <= p) ) i++;
while ( a[l] > p ) l--;
while ( i < l ) {
intercambia (a, i, l);
while ( a[++i] <= p );
while ( a[--l] > p );
}
intercambia (a, prim, l);
return l;
}
Complejidad
El procedimiento Quicksort rompe la filosofía de caso mejor, peor y medio de los
algoritmos clásicos de ordenación, pues aquí tales casos no dependen de la ordenación
inicial del vector, sino de la elección del pivote.
Como mejor pivote debe escogerse la mediana del vector, lo que ocurre es que
encontrarla hace que el algoritmo se vuelva más ineficiente en la mayoría de los casos.
Por esa razón como pivote suele escogerse un elemento cualquiera, a menos que se
conozca la naturaleza de los elementos a ordenar.
En nuestro caso, como a priori suponemos equiprobable cualquier ordenación inicial del
vector, hemos escogido el primer elemento del vector, que es el que se le pasa como
segundo argumento a la función “pivote”. Esta elección conlleva a tres casos
desfavorables para el algoritmo, en donde la partición se realiza de forma totalmente
descompensada, lo que hace que la complejidad sea cuadrática: cuando los elementos son
todos iguales, y cuando el vector está inicialmente ordenado en orden creciente o
decreciente.
5
1.8 ORDENACIÓN SHELLSORT
//@ pre: (0<= prim) && (prim<= ult) && (ult<a.length)
public static void incrementos (int [] a, int prim, int ult) {
int n = ult-prim;
int h = 4;
int i, j, v;
while ( h <= n ) h = 3*h +1; // construimos la secuencia
while ( h >= 1 ) {
for ( i = h; i <= n; i++ ) {
v = a[i];
j = i;
while ( ( j >= h ) && ( a[j-h+prim] > v ) ) {
a[j + prim] = a[j-h+prim];
j = j-h;
}
a[j + prim] = v;
}
h = h/3;
}
}
Este programa utiliza la secuencia: ..., 1093, 364, 121, 40, 13, 1.
Complejidad
Este método es diferente al resto de los procedimientos vistos en este capítulo. Su
complejidad es difícil de calcular y depende mucho de la secuencia de incrementos que
utilice. Por ejemplo, para la secuencia dada existen dos conjeturas en cuanto a su orden
de complejidad: nlog2n y n1,25.
Además, este algoritmo no es muy sensible a la ordenación inicial del vector.
6
Descargar