AlgoritmosDeOrdenacion
AlgoritmosDeOrdenacion
Unidad #: 3
Elaborado por:
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.
2. Algoritmos de ordenación
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.
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.
Ejemplo
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
2
#define TAM 10
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).
Ejemplo
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define TAM 100
#define false 0
#define true 1
while ( intercambio ) {
intercambio = false;
i++;
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.
5
Ejemplo:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define TAM 10
aux = a[minimo];
a[minimo] = a[i];
a[i] = aux;
}
}
int main() {
int array[TAM];
srand((unsigned int)time(NULL));
generarArray( array, TAM );
return 0;
}
6
Ejecución:
7
Ejemplo:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define TAM 10
a[k+1] = x;
}
}
int main() {
int array[TAM];
srand((unsigned int)time(NULL));
generarArray( 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.
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
10
do{
while(lista[izq] < mitad && izq < sup) izq++;
while(mitad < lista[der] && der > inf) der--;
int main() {
int lista[TAM];
srand((unsigned int)time(NULL));
generarArray( lista, TAM );
return 0;
}
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.
- 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.
- 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