0% encontró este documento útil (0 votos)
1 vistas15 páginas

AlgoritmosDeOrdenacion

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
1 vistas15 páginas

AlgoritmosDeOrdenacion

Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 15

Universidad Nacional Autónoma de Nicaragua, León

Facultad de Ciencias y Tecnología


Departamento de Computación

Componente: Aplicaciones de Estructuras de Datos

Unidad #: 3

TEMA: Algoritmos de ordenación

Elaborado por:

 Lic. Luis Ernesto Díaz Beteta

“A la libertad por la Universidad”


Contenido
1. Introducción .......................................................................................................................... 1
2. Algoritmos de ordenación ..................................................................................................... 1
2.1. Método de la burbuja ................................................................................................... 2
2.2. Burbuja mejorada.......................................................................................................... 4
2.3. Método de selección ..................................................................................................... 5
2.4. Método de inserción ..................................................................................................... 7
2.5. Quicksort ....................................................................................................................... 9
2.5.1. Análisis del rendimiento de Quicksort .................................................................... 11
1. Introducción

La ordenación es una aplicación fundamental en computación. La mayoría de los


datos producidos por un programa están ordenados de alguna manera, y muchos de
los cómputos que tiene que realizar un programa son más eficientes si los datos sobre
los que operan están ordenados.

Uno de los tipos de cómputo que más se benefician de operar sobre un conjunto de
datos ordenados es la búsqueda de un dato: encontrar el número de teléfono de una
persona en la guía telefónica es una tarea muy simple y rápida si conocemos su
nombre, ya que están ordenados alfabéticamente.

Un factor clave en cualquier algoritmo de ordenación es su complejidad


computacional, y el cómo esta depende del número de datos a procesar, es importante
contar con algoritmos que no degraden considerablemente su rendimiento con el
tamaño del conjunto de datos.

2. Algoritmos de ordenación

La ordenación o clasificación es un proceso de organizar un conjunto de datos en


algún orden o secuencia creciente o decreciente para datos numéricos o el orden
alfabético para datos compuestos por caracteres. Los algoritmos de ordenación
permutan los elementos del conjunto de datos hasta conseguir dicho orden. Para ello
se basan en dos operaciones básicas: la comparación y el intercambio.

Existen muchos algoritmos de ordenación con diferentes ventajas e inconvenientes; en


este tema veremos los más comunes.

De acuerdo con el tipo de elemento que se quiera la ordenación puede ser:


a) Interna. Los datos se encuentran en memoria (arreglos, listas, etc.) y pueden ser de
acceso aleatorio o directo.
b) Externa. Los datos están en un dispositivo de almacenamiento externo (archivos) y
su ordenación es más lenta que la interna.

1
Los métodos de ordenación interna se suelen dividir en dos grandes grupos:
a) Directos: Burbuja, Selección, Inserción Directa, etcétera.
b) Indirectos: Shell, Ordenación Rápida, Ordenación por Mezcla.

2.1. Método de la burbuja

Su nombre se debe a que el elemento cuyo valor es mayor sube a la posición final del
array, al igual que las burbujas de aire en un depósito suben a la parte superior. Para
ello debe realizar un recorrido paso a paso desde su posición inicial hasta la posición
final del array.

Este método de ordenación se basa en recorrer el array ("realizar una pasada") un


cierto número de veces, comparando pares de valores que ocupan posiciones
adyacentes (el primero con el segundo, el segundo con el tercer, el tercero con el
cuarto,…). Si ambos datos no están ordenados, se intercambian. Esta operación se
repite n-1 veces, siendo n el tamaño del conjunto de datos. Tal como se muestra en la
siguiente imagen:

Ejemplo
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

2
#define TAM 10

void imprimirArray(int *a) {


int i;
for(i = 0; i < TAM-1; i++)
printf( "%d, ", a[i]);
printf( "%d\n", a[i]);
}

void generarArray( int *a ){


int i;
for(i = 0; i < TAM; i++)
a[i] = (int)(rand() % 100);
}

void ordenarBurbuja( int *a ){


int aux, pasada, i;

for ( pasada = 1; pasada < TAM; pasada++ ) {


for ( i = 0; i < TAM - 1; i++ ) {
if ( a[ i ] > a[ i + 1 ] ) {
aux = a[ i ];
a[ i ] = a[ i + 1 ];
a[ i + 1 ] = aux;
}
}
}
}

int main() {
int array[TAM];

srand((unsigned int)time(NULL));

generarArray( array );
printf( "Antes de ordenar\n---------------------\n");
imprimirArray( array );
ordenarBurbuja ( array );
printf( "\nDespués de ordenar\n------------------\n");
imprimirArray( array );

return 0;
}

Ejecución:

3
La complejidad computacional de éste algoritmo O(TAM2).

2.2. Burbuja mejorada


Existe una forma muy obvia para mejorar el algoritmo de la burbuja. Basta con tener
en cuenta la posibilidad de que el conjunto esté ordenado en algún paso intermedio. Si
el bucle interno no necesita realizar ningún intercambio en alguna pasada, el conjunto
estará ya ordenado.

Ejemplo
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define TAM 100
#define false 0
#define true 1

typedef int bool;

void imprimirArray(int *a) {


int i;
for(i = 0; i < TAM-1; i++)
printf( "%d, ", a[i]);
printf( "%d\n", a[i]);
}

void generarArray( int *a ){


int i;
for(i = 0; i < TAM; i++)
a[i] = (int)(rand() % 100);
}

void ordenarArray( int *a ){


int i = 0;
bool intercambio = true;
int actual, temp;

while ( intercambio ) {
intercambio = false;
i++;

for (actual=0; actual<TAM-i; actual++) {


if (a[actual] > a[actual+1]){
intercambio = true;
temp = a[actual];
a[actual] = a[actual+1];
a[actual+1] = temp;
}
}
}
}

4
int main() {
int array[TAM];

srand((unsigned int)time(NULL));

generarArray( array );
printf( "Antes de ordenar\n---------------------\n");
imprimirArray( array );
ordenarArray( array );
printf( "\nDespués de ordenar\n------------------\n");
imprimirArray( array );

return 0;
}

En el mejor caso (si ya está ordenado) realiza TAM-1 comparaciones. En el peor caso
(el elemento menor estaba situado al fin del array) se necesitan las mismas pasadas
que antes y el orden es TAM2. En el caso medio el orden es proporcional a TAM2/2.

2.3. Método de selección

El ordenamiento por selección mejora


el ordenamiento burbuja haciendo un
sólo intercambio por cada pasada a
través de la lista. Para hacer esto, un
ordenamiento por selección busca el
valor mayor a medida que hace una
pasada y, después de completar la
pasada, lo pone en la ubicación
correcta. Al igual que con un
ordenamiento por burbuja, después de
la primera pasada, el ítem mayor está
en la ubicación correcta.

Después de la segunda pasada, el


siguiente mayor está en su ubicación.
Este proceso continúa y requiere n−1
pasadas para ordenar los n ítems, ya
que el ítem final debe estar en su lugar
después de la (n−1)-ésima pasada.

Como se muestra en la siguiente


imagen de la derecha.

5
Ejemplo:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define TAM 10

void imprimirArray(int *a, int n) {


int i;
for(i = 0; i < n-1; i++)
printf( "%d, ", a[i]);

printf( "%d\n", a[i]);


}

void generarArray( int *a, int n ){


int i;
for(i = 0; i < n; i++)
a[i] = (int)(rand() % 100);
}

void ordenarSeleccion( int *a, int n ){


int minimo,i,j, aux;

for(i=0 ; i<n-1 ; i++) {


minimo=i;

for(j=i+1 ; j<n ; j++){


if ( a[minimo] > a[j] )
minimo=j;
}

aux = a[minimo];
a[minimo] = a[i];
a[i] = aux;
}
}

int main() {
int array[TAM];

srand((unsigned int)time(NULL));
generarArray( array, TAM );

printf( "Antes de ordenar\n---------------------\n");


imprimirArray( array, TAM );

ordenarSeleccion( array, TAM );

printf( "\nDespués de ordenar\n------------------\n");


imprimirArray( array, TAM );

return 0;
}

6
Ejecución:

El número de comparaciones que realiza este algoritmo es independiente de la


ordenación inicial. El bucle interno hace TAM-1 comparaciones la primera vez, TAM-2
la segunda,..., y 1 la última. El bucle externo hace TAM-1 búsquedas. El total de
comparaciones es (TAM2-TAM)/2. Por tanto el orden de complejidad es cuadrático
(O(TAM2)).

2.4. Método de inserción

El algoritmo para este método de ordenaci6n es el siguiente: inicialmente, se ordenan


los dos primeros elementos, luego se inserta el tercer elemento en la posición correcta
con respecto a los dos primeros, a continuación se inserta el cuarto elemento en la
posici6n correcta con respecto a los tres primeros elementos ya clasificados y as!
sucesivamente hasta llegar al último elemento, repitiéndose el proceso TAM-1 veces.

Para colocar el dato en su lugar, se debe


encontrar la posición que le corresponde en la
parte ordenada y hacerle un hueco de forma
que se pueda insertar. Para encontrar la
posición se puede hacer una búsqueda
secuencial desde el principio del conjunto hasta
encontrar un elemento mayor que el dado. Para
hacer el hueco hay que desplazar los
elementos pertinentes una posición a la
derecha. Este proceso se describe en la
imagen a la derecha.

El orden de complejidad de este algoritmo es


cuadrático (O(TAM2)).

7
Ejemplo:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define TAM 10

void imprimirArray(int *a, int n) {


int i;
for(i = 0; i < n-1; i++)
printf( "%d, ", a[i]);

printf( "%d\n", a[i]);


}

void generarArray( int *a, int n ){


int i;
for(i = 0; i < n; i++)
a[i] = (int)(rand() % 100);
}

void ordenarInsercion( int *a, int n ){


int i, x, k;

for (i = 1; i < n; i++)/* desde el segundo elemento */


{
x = a[i];
k = i-1;
while (k >= 0 && x < a[k]){
a[k+1] = a[k];
k--;
}

a[k+1] = x;
}
}

int main() {
int array[TAM];

srand((unsigned int)time(NULL));
generarArray( array, TAM );

printf( "Antes de ordenar\n---------------------\n");


imprimirArray( array, TAM );

ordenarInsercion( array, TAM );

printf( "\nDespués de ordenar\n------------------\n");


imprimirArray( array, TAM );

return 0;
}

8
Ejecución:

2.5. Quicksort
El método de ordenación rápida (Quicksort) para ordenar los elementos se basa en el
hecho de que es más rápido y fácil ordenar dos listas pequeñas que una lista grande,
su nombre se debe a que este método, en general, puede ordenar una lista de datos
mucho más rápido que cualquier otro método de los presentados.

El método se basa en la estrategia típica de "divide y vencerás". La lista de elementos


a ordenar se divide en dos partes: una contendrá todos los valores menores o iguales
a un cierto valor (que se suele denominar pivote) y la otra con los valores mayores que
dicho valor. El primer paso es dividir la lista original en dos sublistas y un valor que
sirve de separación, esto es, el pivote. De forma que tenemos tres partes
a. La parte izquierda, que contendrá valores inferiores o iguales al pivote.
b. El pivote.
c. La parte derecha, que contiene valores superiores o iguales al pivote.

Inicialmente, las partes izquierda y derecha no estarán ordenadas, excepto en el caso


de que estén compuestas por un único elemento. Consideremos, por ejemplo, la lista
de valores:
28 21 37 23 19 14 26
Elegimos como pivote del 23 (ubicado al centro de la lista). Recorremos la lista desde
la izquierda y buscamos un elemento mayor que 23 (encontramos el 28). A
continuación, recorremos la lista en sentido descendente empezando por el extremo
derecho y buscamos un valor menor que 23 (encontramos el 14). Se intercambian esto
los valores y se produce la lista:
14 21 37 23 19 28 26
Se sigue recorriendo la lista por la izquierda y se encuentra otro número que es mayor
que 23: el 37; continuamos al recorrido por la derecha y encontramos otro valor menor
que 23: el 19. Volvemos a cambiar sus posiciones:

9
14 21 19 23 37 28 26
En este punto todo los valores que están a la izquierda del pivote son menores que él,
y todos los que están a su derecha son mayores. Pero ninguno de los dos
subconjuntos de valores está ordenado.

En este momento procesamos cada uno de los dos subconjuntos de valores del
mismo modo que el inicial: elegimos un valor de pivote y lo ordenamos de tal modo
que todo los que sean mayores que el pivote estén a la derecha y todos los que sean
menores a la izquierda. De modo que:
• El subconjunto de elementos menores que el pivote se ordenará correctamente, en
virtud del proceso recursivo.
• El mayor elemento en el subconjunto de elementos menores no es mayor que el
pivote, debido a cómo se realiza la partición.
• El menor elemento en el subconjunto de elementos mayores no es menor que el
pivote, debido a cómo se realiza la partición.
• El subconjunto de elementos mayores se ordena correctamente, en virtud del
proceso recursivo.

Ejemplo
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define TAM 10

void imprimirArray(int *lista, int n) {


int i;
for(i = 0; i < n-1; i++)
printf( "%d, ", lista[i]);
printf( "%d\n", lista[i]);
}

void generarArray( int *lista, int n ){


int i;
for(i = 0; i < n; i++)
lista[i] = (int)(rand() % 100);
}

void QuickSort(int lista[], int inf, int sup) {


int izq, der;
int mitad, x;

izq = inf; der = sup;


mitad = lista[(izq + der)/2];

10
do{
while(lista[izq] < mitad && izq < sup) izq++;
while(mitad < lista[der] && der > inf) der--;

if(izq <= der){


x = lista[izq], lista[izq] = lista[der], lista[der] = x;
izq++;
der--;
}
}while(izq <= der);

if(inf < der) QuickSort(lista, inf, der);


if(izq < sup) QuickSort(lista, izq, sup);
}

int main() {
int lista[TAM];

srand((unsigned int)time(NULL));
generarArray( lista, TAM );

printf( "Antes de ordenar\n---------------------\n");


imprimirArray( lista, TAM );

QuickSort( lista, 0, TAM-1 );

printf( "\nDespués de ordenar\n------------------\n");


imprimirArray( lista, TAM );

return 0;
}

2.5.1. Análisis del rendimiento de Quicksort

El mejor caso para Quicksort se presenta cuando el pivote divide al conjunto en dos
subconjuntos de igual tamaño. En este caso tendremos dos llamadas recursivas con
un tamaño igual a la mitad del original y el tiempo de ejecución es O(TAM*log (TAM2)).

Una elección muy popular para el elemento pivote, que puede degradar notablemente
el rendimiento del algoritmo, es emplear el primer elemento de la izquierda. Este pivote
es aceptable si la entrada es completamente aleatoria, pero si la entrada ya está
ordenada, o está ordenada en orden inverso, este pivote proporciona la peor división
posible. Una elección más razonable es elegir como elemento pivote el elemento
central de del array. Lo ideal, sería elegir el valor mediana del array; esto es, aquel

11
valor que ordenando en orden decreciente o creciente la lista de elementos quedaría
justo en el medio (por tanto, este es el pivote ideal).
Otro factor que afecta de modo crítico la eficiencia del algoritmo es qué hacer con
aquellos datos que son iguales al pivote. Si, por ejemplo, sistemáticamente colocamos
los datos que son mayores que el pivote en el subconjunto de la derecha esto puede
llevarnos a realizar particiones desproporcionadas cuando un dato se repite un número
excesivo de veces. El caso extremo será cuando todos los elementos de la lista a
particionar sean iguales al pivote.

En este hipotético caso la partición sería la peor de las posibles: una lista estaría vacía
y la otra contendría todos los elementos.

A simple vista, podría parecer absurdo que un programa informático tuviera que
ordenar una lista de valores iguales. Sin embargo, no lo es tanto: supongamos que
tenemos que ordenar una lista con un millón de datos enteros comprendidos entre el 0
y 999. Si realizamos la ordenación mediante Quicksort y suponemos una distribución
uniforme de los datos llegará un momento en el que, tras varias llamadas recursivas,
nuestro problema consista en ordenar 1000 listas de, aproximadamente, unos 1000
valores cada una de ellas. Estos valores serán en su mayor parte iguales: habrá una
lista con todo 0s, una con todo 1s,..., y una con todo 999s. Llegado este momento, es
obvia la importancia de que nuestra implementación de Quicksort gestione
adecuadamente conjuntos de datos iguales.

La mejor estrategia en este caso es intercambiar los elementos iguales que estén a
ambos lados del pivote: si al buscar un elemento menor que el pivote en el
subconjunto de la izquierda encontramos un elemento igual al pivote lo
intercambiaremos por un elemento mayor o igual que el pivote del subconjunto de la
derecha. A pesar del aparente derroche de operaciones que, en caso de que ambos
elementos intercambiados sean iguales, no hace nada en la práctica, es mejor
gestionar esta situación de este modo y no añadir código adicional que cheque la
ocurrencia de esta situación y que, inevitablemente, penalizaría la eficiencia del
algoritmo.

12
Ejercicio propuesto
Leeme está preparando una aplicación para los amantes de los libros. Este tipo de
aplicación asigna a usuarios y autores un identificador único, un entero. Además, usan
como identificador único de un libro, su ISBN, que actualmente es un entero de 13
dígitos.

Leeme. quiere ir implementando la búsqueda de un libro por ISBN pero todavía no


tiene claro cómo serán la estructura interna de los datos de su aplicación, así que,
como primer paso, nos encomienda que implementemos dicha búsqueda con un array
de enteros y que, antes, ordenemos el array.

Los pasos que debes seguir son los siguientes:

- Define en un fichero llamado insertionSortandSearch.c, un tamaño de array.


Para ello usa la directiva #define, escribiendo por ejemplo #define SIZE 20
luego declara en tu main un array de tipo int de tamaño SIZE.

- Crear una función con el siguiente prototipo: void randomg(int *array, int size,
int max_value, int min_value); para rellenar el array con valores aleatorios. La
función recibirá el array, el número de miembros de dicho array y los valores
máximo y mínimo.

- Implementa la función void print_array(int *array, int size) que imprime un


array de enteros de tamaño size. Usa esta función en tu main para comprobar
que se ha rellenado bien el array.

- Para ordenar el array vamos a implementar un algoritmo de ordenación. Uno


de los más sencillos: el algoritmo de ordenación por inserción. Deberás
implementar dicho algoritmo en la función void insertion_sort(int *array, int
size).

- Ahora queremos buscar un elemento dentro del array ya ordenado. Además,


también queremos saber cuántas iteraciones hacen falta para encontrar dicho
elemento. En este apartado implementaremos la búsqueda recorriendo el array
hasta que encontremos el número. Cuando lo encontremos, paramos y
devolvemos el índice donde lo hemos encontrado. Devuelve un -1 si no lo
hemos encontrado.

- La función se llamará int search_number(int num, int *array, int size, int
*n_iter). Recibe el número a buscar, el array, el número de elementos del
array y un puntero donde dejar el número de iteraciones. Devuelve el índice. Si
no lo encuentra, devuelve un -1.

13

También podría gustarte