Capítulo 14. Algoritmos de ordenación y de búsqueda. 1. Introducción. 2. Métodos de ordenación. 2.1. Método de inserción directa. 2.2. Método de selección directa. 2.3. Método de intercambio directo o de la burbuja. 2.4. Método Quicksort. 3. Métodos de búsqueda. 3.1. Búsqueda directa. 3.2. Búsqueda directa con datos ordenados. 3.3. Búsqueda binaria o dicotómica. 1 1. Introducción. Uno de los procesos más usuales en el tratamiento de datos es la ordenación de los mismos. Lógicamente, los datos se ordenan para acelerar su posterior localización. Cuando el criterio de ordenación se basa en una variable numérica, obviamente un número es mayor que otro si su valor es más grande. Cuando se trata de una variable alfanumérica, una será mayor que otra si por orden alfabético es posterior. Debe recordarse que el orden que se aplica es el establecido en el código ASCII. Por ejemplo, el carácter ‘d’ es mayor que el ‘Z’, ya que las mayúsculas son todas menores que las minúsculas. Por otra parte, para comparar cadenas de caracteres tenemos la función strcmp() que fue explicada en un capítulo anterior. La búsqueda de un dato dentro de un conjunto es otro de los procesos más habituales en el tratamiento de información. Como vamos a ver, si los datos están ordenados, la localización de uno de ellos puede acelerarse. 2. Métodos de ordenación. En todos los métodos que vamos a comentar se supone que tenemos un array de N elementos y lo queremos ordenar de menor a mayor. El índice del array se moverá entre 0 y N-1. Estos métodos podrán aplicarse a estructuras de datos diferentes de los arrays, como por ejemplo los registros de un fichero, siendo el algoritmo idéntico y cambiando simplemente los nombres de algunas variables. 2.2. Método de inserción directa. Consiste en buscar para cada elemento, desde el elemento 0 en adelante, el puesto que le corresponde entre los elementos ya ordenados. Cuando se va a localizar el puesto del elemento i, los elementos anteriores del 0 al i-1 ya estarán ordenados. El proceso sería: - i vale 0: cuando sólo se considera un elemento (valor 19), está en su puesto. i=0 1 2 3 4 19 5 13 4 7 - i pasa a 1: consideramos dos elementos; se busca el puesto del elemento i, es decir del elemento 1 (valor 5); si el puesto es el 0, se desplaza el elemento 0 (valor 19) una posición, insertando el elemento 1 en la posición 0. 0 5 i=1 2 3 4 19 13 4 7 - i pasa a 2: consideramos 3 elementos; se busca el puesto del elemento i, es decir del elemento 2 (valor 13); el puesto se busca desde la posición 0 hasta la i-1, o sea desde 0 a 1; al 13 le corresponde el puesto 1, por lo que se desplaza 2 desde esa posición hasta i los elementos del array (sólo el 19 en este ejemplo) y se inserta el 13 en la posición 1. 0 5 1 i=2 3 4 13 19 4 7 - i pasa a 3: consideramos 4 elementos; se busca el puesto del elemento i, es decir del elemento 3 (valor 4); el puesto se busca desde la posición 0 hasta la i-1, o sea desde 0 a 2; al 4 le corresponde el puesto 0, por lo que se desplaza desde esa posición hasta i los elementos del array (el 5, el 13 y el 19 en este ejemplo) y se inserta el 4 en la posición 0. 0 1 4 5 2 i=3 4 13 19 7 - i pasa a 4: consideramos 5 elementos; se busca el puesto del elemento i, es decir del elemento 4 (valor 7); el puesto se busca desde la posición 0 hasta la i-1, o sea desde 0 a 3; al 7 le corresponde el puesto 2, por lo que se desplaza desde esa posición hasta i los elementos del array (el 13 y el 19 en este ejemplo) y se inserta el 7 en la posición 2. 0 1 2 4 5 7 3 i=4 13 19 - i pasa a 5: como i es igual a N (el número de elementos del array), ya finaliza el proceso. Por tanto, se trata de buscar el puesto de N elementos, es decir un bucle de N iteraciones (i de 0 a N-1). Para cada iteración, es decir para cada elemento i, debe buscarse su puesto recorriendo el array hasta i, o sea este bucle usará una j desde 0 a i. Cuando se conozca el puesto de un elemento i, deberán desplazarse todos los elementos una posición. Los elementos a desplazar son desde el puesto encontrado hasta i. El elemento i se insertará en el hueco libre que ha quedado al desplazar los elementos. Ej. N vale 5. #define N 5 ... int A[N] = {19, 5, 13, 4, 7}; int i, j, PuestoI, ElementoInsertar; for (i = 0; i < N; i++) //Para cada elem. del array { PuestoI = 0; //En principio el puesto es 0 for (j = 0; j < i; j++) if (A[i] > A[j]) //Cada vez que el elem. es PuestoI++; //mayor, tiene un puesto más. ElementoInsertar = A[i]; //Usa variable auxiliar. 3 for (j = i-1; j >= PuestoI; j--) //El bucle debe ir A[j+1] = A[j]; //hacia atrás (j--), para no //sobrescribir los elementos. A[PuestoI] = ElementoInsertar; //Inserta elemento. } Este algoritmo también funciona cuando un mismo valor aparece repetido en el array. 2.2. Método de selección directa. Este método consiste en recorrer el array completo y para cada iteración i localizar el menor elemento. Una vez localizado, se intercambia el elemento menor con el que está en la posición i. Por tanto, se trata simplemente de buscar el menor elemento entre los que quedan por ordenar, es decir desde i hasta N-1. Ej. N vale 5. #define N 5 ... int A[N] = {19, 5, 13, 4, 7}; int i, j, PuestoMenor, ElementoIntercambio; for (i = 0; i < N; i++) //Para cada elem. del array { PuestoMenor = i; //Inicialmente el puesto es donde //está el elem. i. for (j = i+1; j < N; j++) //Recorre los que quedan //por ordenar. if (A[j] < A[PuestoMenor]) PuestoMenor = j; ElementoIntercambio = A[i]; A[i] = A[PuestoMenor]; A[PuestoMenor] = ElementoIntercambio; } Este método no aplica el bucle de desplazamiento que el método anterior aplicada para cada elemento del array. 2.3. Método de intercambio directo o de la burbuja. Este método consiste en recorrer el array completo. Cada elemento i intercambia con el anterior mientras el elemento i sea menor. Esto implica que para cada elemento i deberá recorrerse el array desde i-1 hasta 0 o hasta que se encuentre un elemento menor que el i. De este modo cada elemento va subiendo en el array (burbuja), ya que se va intercambiando con el anterior, hasta quedar en su puesto. Cuando se va a procesar el elemento i, debe tenerse en cuenta que todos los elementos anteriores ya estarán ordenados. 4 Ej. N vale 5. #define N 5 ... int A[N] = {19, 5, 13, 4, 7}; int i, j, Aux; //Variable auxiliar para intercambiar. for (i = 0; i < N; i++) { for (j = i-1; j >= 0 && A[j] > A[j+1]; j--) { Aux = A[j]; //Se va intercambiando el elemento A[j]= A[j+1]; //con el anterior mientras estén A[j+1] = Aux; //desordenados. } } 2.4. Método Quicksort. Este método está considerado actualmente el mejor algoritmo de ordenación. Como vamos a ver, se puede implementar usando recursividad. Consiste en elegir un elemento cualquiera del array, por ejemplo el que ocupa la posición central, y en una primera pasada colocar todos los elementos mayores después de ese elemento y todos los elementos menores antes de dicho elemento central. Se aplicará de nuevo el proceso a cada mitad del array, llamando a la misma función, de forma recursiva. Es decir, se ejecuta el mismo algoritmo sobre el trozo de array donde han quedado los elementos menores y por otra parte sobre el trozo de array con los elementos mayores. El algoritmo finaliza cuando el número de elementos del trozo de array que se va a procesar sea menor o igual a 0, es decir que el trozo de array no tenga elementos. Ej. N vale 5. #define N 5 void QuickSort(int A[], int Desde, int Hasta); void main(void) { int A[N] = {19, 5, 13, 4, 7}; int i; QuickSort(A, 0, N-1); } void QuickSort(int A[], int Desde, int Hasta) { int Izq, Der, Mitad, Aux; Izq = Desde; 5 Der = Hasta; Mitad = A[(Izq+Der) / 2]; do { for ( ;Izq <= Der && A[Izq] < Mitad; Izq++); for ( ;Der >= Izq && A[Der] > Mitad; Der--); if (Izq <= Der) { Aux = A[Izq]; A[Izq] = A[Der]; A[Der] = Aux; Izq++; Der--; } } while (Izq <= Der); if (Izq < Hasta) QuickSort(A, Izq, Hasta); if (Der > Desde) QuickSort(A, Desde, Der); } 3. Métodos de búsqueda. Como en el caso de la ordenación, vamos a suponer que tenemos un array de N elementos. El índice del array se moverá entre 0 y N-1. El algoritmo de búsqueda debe indicarnos si un valor determinado está en el array y en qué posición o si por el contrario no está. Los métodos de búsqueda que vamos a ver podrán aplicarse a estructuras de datos diferentes de los arrays. El algoritmo será el mismo, sólo cambiarán los nombres de algunas variables. Cuando los datos están ordenados, los algoritmos de búsqueda pueden ser diferentes y más rápidos. 3.1. Búsqueda directa. Este es el método más sencillo y consiste en ir comparando el valor buscado con los sucesivos valores del array, hasta que sean iguales (es localizado) o hasta que finalice el array (no es localizado). No es necesario que los datos del array estén ordenados. Ej. N vale 5. #define N 5 void main(void) { int A[N] = {19, 5, 13, 4, 7}; int i, ValorBuscar; scanf(“%2d”, &ValorBuscar); fflush(stdin); for (i = 0; i < N && ValorBuscar != A[i]; i++) { ; } //El bucle for sólo incrementa la i. if (i < N) printf(“Encontrado en posición %d”, i); else 6 printf(“No encontrado”); } Debe tenerse en cuenta que si se utiliza después del bucle la condición “if (ValorBuscar == A[i])...” no es del todo correcto, ya que cuando el valor buscado no se localiza, la i acaba en el valor N y pudiera dar la casualidad que el valor de A[N] coincidiera con ValorBuscar. A[N] es el valor después del array en memoria, ya que el array llega hasta N-1. 3.2. Búsqueda directa con datos ordenados. Si los datos del array están ordenados, por ejemplo de menor a mayor, cuando se llega a un valor del array que sea mayor que el buscado, la búsqueda finaliza, ya que los valores sucesivos del array serán aún mayores, por estar ordenado el array de menor a mayor. Ej. N vale 5. #define N 5 void main(void) { int A[N] = {19, 5, 13, 4, 7}; int i, ValorBuscar; scanf(“%2d”, &ValorBuscar); fflush(stdin); for (i = 0; i < N && ValorBuscar > A[i]; i++); { ; } //El bucle for sólo incrementa la i. if (i < N && ValorBuscar == A[i]) printf(“Encontrado en posición %d”, i); else printf(“No encontrado”); } 3.3. Búsqueda binaria o dicotómica. Para utilizar este método es necesario que los datos estén ordenados. El algoritmo consiste en comprobar si el valor es menor o mayor que el valor central del array o mayor. Si es menor indica que el valor buscado se encuentra en la primera mitad del array, aplicándose el mismo método a esa mitad, descartándose la segunda mitad. Si es mayor indica que el valor buscado se encuentra en la segunda mitad del array y por tanto se aplicará el mismo método a esa segunda mitad, descartándose la primera mitad. Las comparaciones con el valor central se hacen hasta que coincida con el valor buscado o hasta que la mitad del array no tenga elementos. Ej. N vale 5. #define N 5 int BusquedaBinaria(int [], int, int, int); void main(void) { int A[N] = {1,6,7,22,25}; int i, ValorBucar; 7 scanf("%d", &Valor); fflush(stdin); i = BusquedaBinaria(A, 0, N-1, ValorBuscar); if (i == -1) printf("\n No localizado"); else printf("\n Localizado en posición %d", i); } int BusquedaBinaria(int A[], int Desde, int Hasta, int ValorBuscar) { int enc = -1; int izq = Desde, der = Hasta, cen = (der+izq) / 2; while (ValorBuscar != A[cen] && izq < der) { if (ValorBuscar > A[cen]) izq = cen + 1; else if (ValorBuscar < A[cen]) der = cen - 1; cen = (der + izq) / 2; } if (ValorBuscar == A[cen]) enc = cen; return(enc); } Esta función puede implementarse de forma recursiva. int BusquedaBinaria(int A[], int Desde, int Hasta, int ValorBuscar) { int enc = -1; int cen = (Hasta + Desde) / 2; if (Hasta >= Desde) { if (Valor > A[cen]) enc = Buscar(A, cen+1, Hasta, Valor); else if (Valor < A[cen]) enc = Buscar(A, Desde, cen-1, Valor); else enc = cen; } return(enc); } 8 EJERCICIOS - CAPITULO 14: Realizar los siguientes algoritmos en pseudocódigo y en C. 1. Añadir datos de artículos a un fichero binario con los campos código (long), descripción y precio. Después ordenarlo por código utilizando el método de la burbuja. 2. Ordenar el fichero del ejercicio anterior por descripción aplicando el método de selección directa. 3. Buscar artículos por código en el fichero que está ordenado por código usando el método de búsqueda directa en datos ordenados. 4. Buscar artículos por descripción aplicando la búsqueda binaria, lógicamente sobre el fichero ordenado por descripción. 9