Sie sind auf Seite 1von 7

Algoritmos de ordenacin: Lo bsico

Los algoritmos de ordenacin, como su propio nombre apunta, sirven para ordenar datos. Hay unos cuantos, y son bien conocidos y estudiados por la algoritmia... Que si la burbuja, que si el quicksort... La mayor parte de ellos son sencillos de entender y codificar. Pero muchas veces no se utilizan con un criterio claro. En ste artculo vamos a intentar hablar sobre ellos, en qu se basan, qu consideraciones debemos conocer para utilizarlos y una breve clasificacin general. Poco a poco, iremos escribiendo nuevos artculos sobre este tema, uno para cada mtodo de ordenacin de entre los ms populares y simples con ejemplos sencillos en C#.

Qu son?
Vamos a empezar hablando de qu son exactamente los algoritmos de ordenacin. Bsicamente son algoritmos que cogen una lista de datos ordenables y los van cambiando de lugar, de tal manera que cuando el algoritmo finaliza, si recorremos esa lista, los datos mantienen cada uno con el siguiente un cierto orden. Normalmente, se utilizan con arrays, cuyos elementos, en principio, no tienen por qu estar ordenados. Un algoritmo de ordenacin aplicado sobre ese array, cambia de sitio los elementos hasta lograr que estn ordenados.

En qu se basan?
Estos algoritmos se basan en dos operaciones bsicas: La comparacin: el algoritmo de ordenacin tiene que ser capaz de disponer de una operacin tal que dados dos datos cualesquiera, la operacin de comparacin indique si estn en orden o no. El intercambio de datos: el algoritmo debe ser capaz de intercambiar dos elementos de la lista. El hecho de necesitar estas dos operaciones, nos lleva a una serie de consideraciones: Empecemos por la segunda. Primeramente, aclarar que al hablar de lista, simplemente me refiero a un conjunto de valores que estn colocados cada uno en una posicin concreta, y que pueden ser identificados por esa posicin. Por ejemplo, los libros de una librera, vistos de izquierda a derecha forman una lista:

Hay un libro que ocupa la primera posicin, otro la segunda... etc. Sin embargo, si tiro esos libros al suelo y los dejo caer a su aire, no formarn una lista, porque ninguno ocupar una posicin concreta.

Volvamos a los datos. El hecho de que tengamos que poder intercambiar dos valores de una lista implica que leemos uno de ellos y lo copiamos en una variable temporal, luego cogemos el segundo elemento y lo copiamos en la posicin del primero ("machacando" su valor, pero no pasa nada, porque lo hemos cogido en una variable temporal), y finalmente copiamos el valor de la variable temporal (que tena el valor del primero) a la posicin del segundo. Realmente hemos hecho tres operaciones de asignacin de variables, pero el resultado final es que hemos logrado intercambiar valores. Si hablramos de los libros de arriba y quisiera intercambiar, por ejemplo, el segundo con el cuarto, cogera el segundo en mi mano, dejando as un hueco en segundo lugar (el libro est fuera de la lista, en mi mano: mi mano es una variable temporal), pasara el cuarto al sitio del segundo, que ahora est vaco y finalmente, dejara el libro que tengo en mi mano en el lugar del cuarto. (Tambin podra haberlo hecho al revs, sacando el cuarto en primer lugar). Mover un dato de un lugar a otro implica que nuestro conjuto de datos est almacenado en alguna estructura de datos que nos permite obtener un dato y escribirlo en una posicin concreta de la estructura de datos. Ambas operaciones, de lectura y escritura de un dato deben poder realizarse con un coste constante... es decir, el tiempo que se tarde en leer o escribir un dato no debe depender de la cantidad de datos que estemos intentando ordenar, dicho de otro modo, de la cantidad de datos de la lista que manejemos. Esto no se puede tomar a la ligera, ya que hay muchas estructuras de datos capaces de almacenar una lista de valores, pero muy pocas ofrecen un coste de lectura y escritura constante. Tpicamente, la estructura por antonomasia que ofrece esa caracterstica es el array (tambin llamado arreglo o vector, segn la literatura que escojas). En otras estructuras, como listas enlazadas, doblemente enlazadas, etc, cuando se accede a una determinada posicin, antes deben recorrer un buen nmero de posiciones. Es decir, en un array de 10000 elementos, accecer al elemento de la posicin 9000, bien sea para obtener su valor o para asignrselo "tarda" lo mismo que para el de la posicion 900. Sin embargo, en una lista enlazada tpica no: acceder al 9000 tardar bastante ms que acceder al 900, porque internamente, en una lista enlazada, antes de acceder a una posicin, se recorren todas las anteriores. Como los algoritmos de ordenacin hacen muchas de estas operaciones (para lograr los intercambios), si en lugar de un array utilizamos, por ejemplo una lista enlazada, haremos que el coste del acceso a un elemento de la lista enlazada (tpicamente del orden de O(n)) multiplique al propio coste del algoritmo de ordenacin. Atencin Los algoritmos de ordenacin tpicos slo se aplican a estructuras de datos cuyo coste de acceso a un elemento (tanto para leer su valor como para escribir uno nuevo) sea constante. Normalmente, arrays. En cuanto a lo de la comparacin, tambin tiene su importancia. Cuando estudiamos un algoritmo de ordenacin en un libro o cualquier otro texto tcnico, casi siempre nos lo presentan ordenando un vector de enteros. Yo muchas veces me he preguntado Y para qu puetas quiero yo un puado de enteros en orden?... Para nada, normalmente. Realmente, en los libros nos ensean a ordenar un array de enteros porque los enteros es un conjunto que tiene un orden entre sus elementos que tenemos muy asimilado... Si nos presentan dos enteros cualesquiera, inmediatamente sabemos cul de los dos es "menor".

Sin embargo, hay otros casos donde la cosa no est tan clara... por ejemplo, las cadenas. Si te presento estas dos cadenas de texto, Cul de ellas va antes?: "HOLA" y "caracola"... Pues depende... si utilizo el orden alfabtico, debera ir antes "caracola", pero si utilizo el orden de la tabla ASCII? o Unicode?, debera ir antes "HOLA", ya que en estas tablas de carcteres las maysculas van antes que las minsculas. Y an hay ms... Y si en lugar de un dato simples, como enteros o cadenas, estamos intentando ordenar datos compuestos, como structs (registros, en los lenguajes pascaloides) u objetos de una clase. Piensa en esto:
class Persona { int codigo; String nombre; String apellidos; double altura; //altura en metros double peso; //peso en kilogramos }

Qu pasa si tenemos un array de objetos de la clase Persona y queremos ordenarlo?. Detente un momento y reflexiona. Dadas dos personas, por ejemplo, 10 14 Pepe Luisa Prez Prez Martnez Martnez 1.78 1.72 Cul de las dos va antes? ... ... Pues.... Depende !!! En efecto, depende... los objetos de la clase Persona no tienen un orden implcito. Podemos ordenar a las personas por su altura, por sus apellidos, por su nombre, por su cdigo.... y no slo eso, sino por cualquier combinacin de esos datos que se nos ocurra... por ejemplo, por el ndice de masa corporal , que es el peso dividido por la altura al cuadrado. As pues, podemos utilizar los algoritmos de ordenacin para ordenar un conjunto de cualquier tipo de dato, pero necesitaremos un criterio de ordenacin. Es posible (y relativamente frecuente) que una misma lista de datos sea susceptible de ser ordenada por distintos criterios. Cada aplicacin del algoritmo se hace con un nico criterio.

Criterio de ordenacin Antes de aplicar un algoritmo de ordenacin, debemos tener claro un criterio de ordenacin, es decir, una funcin, mtodo u operador tal que dados dos elementos nos diga al menos si estn en orden o no, siempre segn el criterio que hayamos decidido. Por otro lado, si estamos ordenando structs (o records de los lenguajes pascaloides) u objetos, stos ocupan gran cantidad de memoria comparados con los tipos primitivos (enteros, reales...). Realizar los intercambios puede suponer un gran trasiego de bytes. Cuando ste es el caso, normalmente no interesa hacer el intercambio del struct u objeto entero, sino utilizar un array de punteros o referencias, e intercambiar realmente el puntero o la referencia. En lenguajes como C# o Java, donde las referencias son fundamentales (algunos dicen que las referencias son ciudadanos de primer orden), al crear un array cuyos elementos son objetos, stos lenguajes crean realmente un array de referencias a objetos. En otros lenguajes, como C++, en los que las referencias no existen o no son importantes, el programador puede crear un array de objetos o structs, o bien un array de punteros a objetos o structs. En el primer caso (un array de objetos o structs), al aplicar un algoritmo de ordenacin, durante los intercambios se intercambia todo el contenido de una posicin del array, es decir, un objeto o struct completo, con todos sus bytes. En el segundo (un array de punteros a objetos o structs), slo se intercambian los punteros, que normalmente ocupan muy pocos bytes. El objeto o struct sigue en la misma posicin de memoria. Cutre-animacin: Objetos o referencias? Cuando aplicamos un algoritmo de ordenacin sobre un vector, debemos tener claro si estamos intercambiando objetos enteros de posicin (o structs o records) o bien punteros o referencias a stos. Intercambiar punteros o referencias es mucho ms eficiente. Algunos lenguajes manejan todos los objetos con referencias por defecto, y otros manejan los objetos enteros por defecto. Por ltimo, comentar que como en todo, hay excepciones. Algunos algoritmos se ahorran las comparaciones, pero utilizan otras operaciones aritmtico-lgicas, relacionadas con la propia estructura de los datos. Otros algoritmos "simplifican" varias operaciones de intercambio, realizando menos movimientos en memoria.

Para qu NO valen
Hay principalmente dos utilizaciones de los algoritmos de ordenacin que no son nada correctas, y que muchas veces estamos tentados de utilizar: Para ordenar una estructura de datos cuyo coste de acceso a un elemento no sea constante. sto ya lo hemos comentado: acceder al primer elemento de la estructura o al ltimo debe tardar lo mismo. Para insertar un elemento en orden en una estructura cuyos elementos ya estn en orden. Esto lo he visto hacer muchas veces: a la hora de insertar un elemento nuevo en una lista que ya tiene orden, se puede aplicar la idea feliz de aadir el nuevo elemento por el principio o por el final, y luego pasar un algoritmo de ordenacin para que lo coloque en su sitio... Funcionar, funciona... pero con un coste en tiempo absurdo. En estos casos, lo mejor es hacer una insercin lineal (que tiene un coste O(n)), o mejor todava, una insercin

dicotmica. La insercin lineal consiste en ir recorriendo la lista secuencialmente hasta encontrar el lugar que debera ocupar el nuevo elemento. Despus se abre un hueco y se inserta ah el nuevo elemento. La insercin dicotmica consiste en hacer una bsqueda dicotmica para encontar el lugar del nuevo elemento y luego abrir hueco ah e insertar el nuevo elemento.

Taxonoma
Siempre me ha gustado esta palabra: taxonoma . Significa clasificacin, pero dicho de manera ms pedante Vamos a clasificar los algoritmos de ordenacin, por varios criterios

Segn sirvar para ordenar datos en memoria primaria o secundaria


Prcticamente todos los algoritmos de ordenacin pueden aplicarse para ordenar datos en memoria principal (tpicamente en arrays) o en memoria secundaria (discos, disquetes, pendrives...), eso s. siempre que sta ltima sea a travs de ficheros de acceso aleatorio (hoy en da, prcticamente toda). No obstante, la naturaleza de la memoria secundaria es distinta a la principal, an hablando de acceso aleatorio. La memoria secundaria es, en general, varios miles de veces ms lenta en los accesos, y adems, por sus limitaciones fsicas, aunque se pueda acceder aleatoriamente a una determinada parte de un fichero, los dispositivos suelen acceder a grandes bloques de bytes, y es el sistema operativo el que con gran esfuerzo nos da la impresin de que podemos acceder a un byte concreto. As pues, en general los algoritmos de ordenacin no se comportan bien en la memoria secundaria, y para ello han sido creados a propsito algunos que funcionan mucho mejor en esos entornos. As, hablamos de algoritmos de ordenacin Internos: cuando ordenan estructuras almacenadas en memoria principal Externos: cuando ordenan estructuras almacenas en memoria secundaria (ficheros binarios, sean de acceso aleatorio o secuencial).

Segun mantengan en el mismo orden dos datos iguales.


Imagina esta lista de personas y edades: Pepe:42, Luis:21, Antonia:50, Mara:7, Bernardo:21, Catalina:32 Fjate en que si pretendemos ordenar a estas personas por edad de menor a mayor hay dos posibles clasificaciones: Maria:7, Bernardo:21, Luis:21, Catalina:32, Pepe:42, Antonia:50 Maria:7, Luis:21, Bernardo:21, Catalina:32, Pepe:42, Antonia:50 Bernardo y Luis tienen la misma edad... Est claro que deben ocupar la segunda y tercera posicin, pero Cul va antes? Pues habr veces que eso no tenga ninguna importancia y habr veces en las

que s la tenga (pocas, la verdad). Algunos algoritmos de ordenacin no tienen eso en cuenta, y podran ordenar el array de una manera o de la otra indistintamente. Otros, nos garantizan si se presenta un caso como ste, siempre devolveran la segunda solucin, porque en ella va Luis delante, al igual que en la lista original antes de ser ordenada. As pues, hablamos de algoritmos de ordenacin Estables: cuando garantizan que dos elementos que podran ocupar la misma posicin se colocan manteniendo el orden relativo que tenan entre ellos antes de aplicar el algoritmo No estables: cuando el algortimo no garantiza eso, y en caso de que se produzca, los elementos mantendrn una posicin correcta, pero sin orden alguno entre ellos.

Segn utilicen memoria extra


Algunos algoritmos requieren una cantidad de memoria fija temporal independientemente de la cantidad de datos que tengan que ordenar. Otros, por el contrario, requieren una cantidad de memoria temporal que depende en alguna medida de la cantidad de datos que tengan que ordenar. No necesitan memoria extra: cuando la cantidad de memoria temporal es fija, independientemente del nmero de datos a ordenar (a este tipo de algoritmos, a menudo se les llama algoritmos in-situ) S necesitan memoria extra: cuando la cantidad de memoria temporal es variable, y es mayor cuanto mayor sea el nmero de datos a ordenar. El hecho de que sea de un tipo u otro est relacionado en cierta manera con la complejidad del algoritmo. Los algortimos de menor complejidad temporal (en el peor caso) suelen gastar memoria extra.

Segn su complejidad temporal


Los algoritmos de ordenacin estn profundamente estudidados desde el punto de vista de la complejidad temporal. No es algo que deba obsesionar, pero est muy bien conocerla, al menos, en el peor caso. Cuadrtica (O(n2)): es la complejidad de los algorimos ms sencillos N-Logartmica (O(nlogn)): es la complejidad de algunos algo ms avanzados, y que suelen consumir memoria extra. (Aumentan la complejidad espacial y reducen la temporal) Lineales (O(n)): son los que se aprovechan de alguna caracterstica aritmtica de los datos, evitando las comparaciones (una de las excepciones que comentbamos antes)

Segn realicen comparaciones o no


Finalmente, como algunos algoritmos no realizan las comparaciones, pues tambin se pueden clasificar en

Los que realizan comparaciones Los que no lo hacen (ya hemos comentado que se basan en alguna propiedad aritmtica de los datos)

Das könnte Ihnen auch gefallen