Está en la página 1de 12

ALGORITMOS Y ESTRUCTURAS DE DATOS Ordenamiento de Burbuja Es un sencillo algoritmo de ordenamiento.

Funciona revisando cada elemento de la lista que va a ser ordenada con el siguiente, intercambindolos de posicin si estn en el orden equivocado. Es necesario revisar varias veces toda la lista hasta que no se necesiten ms intercambios, lo cual significa que la lista est ordenada. El ordenamiento de burbuja tiene una complejidad (n). Cuando una lista ya est ordenada, el ordenamiento de burbuja pasar por la lista una vez, y encontrar que no hay necesidad de intercambiar las posiciones de los elementos. Por ende, el ordenamiento har solamente n comparaciones y determinar que la lista est completamente ordenada. Adems, usar considerablemente menos tiempo que (n) si los elementos en la lista desordenada no estn demasiado lejos de su ubicacin correcta. El algoritmo hace N2/2 comparaciones. Si la data es aleatoria, se harn intercambios la mitad de las veces: N2/4. Ordenamiento por Seleccin Es un algoritmo de ordenamiento que requiere O(n2) operaciones para ordenar una lista de n elementos. Su funcionamiento es el siguiente:

Buscar el mnimo elemento de la lsta Intercambiarlo con el primero Buscar el mnimo en el resto de la lista Intercambiarlo con el segundo

Y en general:

Buscar el mnimo elemento entre una posicin i y el final de la lista Intercambiar el mnimo con el elemento de la posicin i

Ordenamiento por Insercin Es una manera muy natural de ordenar para un ser humano, y puede usarse fcilmente para ordenar un mazo de cartas numeradas en forma arbitraria. Inicialmente se tiene un solo elemento, que obviamente es un conjunto ordenado. Despus, cuando hay k elementos ordenados de menor a mayor, se toma el elemento k+1 y se compara con todos los elementos ya ordenados, detenindose cuando se encuentra un elemento menor (todos los elementos mayores han sido desplazados una posicin a la derecha). En este punto se inserta el elemento k+1 debiendo desplazarse los dems elementos. Requiere un mximo de N(N 1) / 2 comparaciones. Big-O Notation We express complexity using big-O notation. For a problem of size N:

a constant-time method is "order 1": O(1) a linear-time method is "order N": O(N) a quadratic-time method is "order N squared": O(N2)

Note that the big-O expressions do not have constants or low-order terms. This is because, when N gets large enough, constants and low-order terms don't matter (a constant-time method will be faster than a linear-time method, which will be faster than a quadratic-time method). Formal definition: A function T(N) is O(F(N)) if for some constant c and for values of N greater than some value n0: T(N) <= c * F(N) The idea is that T(N) is the exact complexity of a method or algorithm as a function of the problem size N, and that F(N) is an upper-bound on that complexity (i.e., the actual time/space or whatever for a problem of size N will be no worse than F(N)). In practice, we want the smallest F(N) -- the least upper bound on the actual complexity.

For example, consider T(N) = 3 * N2 + 5. We can show that T(N) is O(N2) by choosing c = 4 and n0 = 2. This is because for all values of N greater than 2: 3 * N2 + 5 <= 4 * N2 T(N) is not O(N), because whatever constant c and value n0 you choose, I can always find a value of N greater than n0 so that 3 * N2 + 5 is greater than c * N. How to Determine Complexities In general, how can you determine the running time of a piece of code? The answer is that it depends on what kinds of statements are used. 1. Sequence of statements statement 1; statement 2; statement k; (Note: this is code that really is exactly k statements; this is not an unrolled loop like the N calls to addBefore shown above.) The total time is found by adding the times for all statements: total time = time(statement 1) + time(statement 2) + ... + time(statement k) If each statement is "simple" (only involves basic operations) then the time for each statement is constant and the total time is also constant: O(1). In the following examples, assume the statements are simple unless noted otherwise. 2. if-then-else statements if (cond) { sequence of statements 1 } else { sequence of statements 2 }

Here, either sequence 1 will execute, or sequence 2 will execute. Therefore, the worst-case time is the slowest of the two possibilities: max(time(sequence 1), time(sequence 2)). For example, if sequence 1 is O(N) and sequence 2 is O(1) the worst-case time for the whole if-then-else statement would be O(N). 3. for loops for (i = 0; i < N; i++) { sequence of statements } The loop executes N times, so the sequence of statements also executes N times. Since we assume the statements are O(1), the total time for the for loop is N * O(1), which is O(N) overall. 4. Nested loops for (i = 0; i < N; i++) { for (j = 0; j < M; j++) { sequence of statements } } The outer loop executes N times. Every time the outer loop executes, the inner loop executes M times. As a result, the statements in the inner loop execute a total of N * M times. Thus, the complexity is O(N * M). In a common special case where the stopping condition of the inner loop is j < N instead of j < M (i.e., the inner loop also executes N times), the total complexity for the two loops is O(N2). Big O Notation Analisys The primary trick is to start thinking about algorithms in terms of a generalized currency, usually called "n". Exactly what n represents depends on each algorithm, and yet, in a brilliant way, can only apply to

any one algorithm in one way, so anyone can understand how it applies to a particular algorithm once they understand what's going on. Although I have thoroughly grasped the notion of what Big O is about, I find it exceedingly difficult to put into words, and thus, I am probably not the best person to try to explain this, but... The first thing that you must understand is what a "constant time" operation is. This is an operation that takes the same amount of time regardless of the circumstances. For example, calculating the square root of a number takes the same amount of time regardless of what the number is (this actually isn't true but ignore that convolution for the moment). You must locate all the constant time operations in your algorithm, say a single collision-detection check between any two objects, or a single call to the renderer, or a single called to play a sound, or a single call to find out the position of the joystick. Actually, calling the renderer may not be constant time for reasons I attempt to describe below. Once you understand constant time operations, you must then understand how loops, tree-traversals, and other iterative and recursive pieces of code use constant time operations. For example, if you have "n" objects in your system and you do brute force collision-detection (you compare every pair of objects), this is clearly an O(n^2) algorithm (actually it's theta(n^2), not O(n^2), but don't worry about that for now because you must perform n-squared constant time operations to perform the algorithm. The thing to remember about all of this, is that the running time of the algorithm is a function of "n", not of seconds, or frames, or clock-cycles, or anything solid like that. It is an algorithmic concept. I don't know if I have explained that very well. The next thing to understand is what the whole point of big O is in the first place. Big O tells you the "worst case running time" of an algorithm, in terms of "n". It does not tell you the best case running time. An algorithm may run in constant time on some inputs and the worst case time on other inputs. Sorting algorithms work like this. Different inputs (different orderings of items) take different amounts of time to sort. Most sort algorithms take O(nlogn) time, which means that to sort a

list of n items, the worst case may require performing approximately nlogn constant time operations, but the best case may perform only n operations, for example, if the list is already perfectly sorted. Los rdenes ms utilizados en anlisis de algoritmos, en orden creciente, son los siguientes (donde c representa una constante y n el tamao de la entrada): notacin O(1) O(log n) O([log n]c) O(n) nombre orden constante orden logartmico orden polilogartmico orden lineal

O(n log n) orden lineal logartmico O(n) O(nc) orden cuadrtico orden polinmico

O(cn), n > 1 orden exponencial O(n!) O(nn) orden factorial orden doblemente exponencial

The common sorting algorithms can be divided into two classes by the complexity of their algorithms. Algorithmic complexity is a complex subject that would take too much time to explain here, but suffice it to say that there's a direct correlation between the complexity of an algorithm and its relative efficiency. Algorithmic complexity is generally written in a form known as Big-O notation, where the O represents the complexity of the algorithm and a value n represents the size of the set the algorithm is run against. For example, O(n) means that an algorithm has a linear complexity. In other words, it takes ten times longer to operate on a set of 100 items than it does on a set of 10 items (10 * 10 = 100). If the complexity was O(n2)

(quadratic complexity), then it would take 100 times longer to operate on a set of 100 items than it does on a set of 10 items. The two classes of sorting algorithms are O(n2), which includes the bubble, insertion, selection, and shell sorts; and O(n log n) which includes the heap, merge, and quick sorts. In addition to algorithmic complexity, the speed of the various sorts can be compared with empirical data. Since the speed of a sort can vary greatly depending on what data set it sorts, accurate empirical results require several runs of the sort be made and the results averaged together. The empirical data on this site is the average of a hundred runs against random data sets on a single-user 250MHz UltraSPARC II. The run times on your system will almost certainly vary from these results, but the relative speeds should be the same the selection sort runs in roughly half the time of the bubble sort on the UltraSPARC II, and it should run in roughly half the time on whatever system you use as well. These empirical efficiency graphs are kind of like golf - the lowest line is the "best". Keep in mind that "best" depends on your situation - the quick sort may look like the fastest sort, but using it to sort a list of 20 items is kind of like going after a fly with a sledgehammer.

O(n2) Sorts

As the graph pretty plainly shows, the bubble sort is grossly inefficient, and the shell sort blows it out of the water. Notice that the first horizontal line in the plot area is 100 seconds - these aren't sorts that you want to use for huge amounts of data in an interactive application. Even using the shell sort, users are going to be twiddling their thumbs if you try to sort much more than 10,000 data items. On the bright side, all of these algorithms are incredibly simple (with the possible exception of the shell sort). For quick test programs, rapid prototypes, or internal-use software they're not bad choices unless you really think you need split-second efficiency.

O(n log n) Sorts

Speaking of split-second efficiency, the O(n log n) sorts are where it's at. Notice that the time on this graph is measured in tenths of seconds, instead hundreds of seconds like the O(n2) graph. But as with everything else in the real world, there are trade-offs. These algorithms are blazingly fast, but that speed comes at the cost of complexity. Recursion, advanced data structures, multiple arrays - these algorithms make extensive use of those nasty things. Estabilidad Los algoritmos de ordenamiento estable mantienen un relativo preorden total. Esto significa que un algoritmo es estable solo cuando hay dos registros R y S con la misma clave y con R apareciendo antes que S en la lista original.

Cuando elementos iguales (indistinguibles entre si), como nmeros enteros, o ms generalmente, cualquier tipo de dato en donde el elemento entero es la clave, la estabilidad no es un problema. De todas formas, se asume que los siguientes pares de nmeros estn por ser ordenados por su primer componente: (4, 1) (3, 7) (3, 1) (5, 6) En este caso, dos resultados diferentes son posibles, uno de los cuales mantiene un orden relativo de registros con claves iguales, y una en la que no: (3, 7) (3, 1) (4, 1) (5, 6) (orden mantenido) (3, 1) (3, 7) (4, 1) (5, 6) (orden cambiado) Los algoritmos de ordenamiento inestable pueden cambiar el orden relativo de registros con claves iguales, pero los algoritmos estables nunca lo hacen. Los algoritmos inestables pueden ser implementados especialmente para ser estables. Una forma de hacerlo es extender artificialmente el cotejamiento de claves, para que las comparaciones entre dos objetos con claves iguales sean decididas usando el orden de las entradas original. Recordar este orden entre dos objetos con claves iguales es una solucin poco prctica, ya que generalmente acarrea tener almacenamiento adicional. Ordenar segun una clave primaria, secundaria, terciara, etc., puede ser realizado utilizando cualquier mtodo de ordenamiento, tomando todas las claves en consideracin (en otras palabras, usando una sola clave compuesta). Si un mtodo de ordenamiento es estable, es posible ordenar mltiples tems, cada vez con una clave distinta. En este caso, las claves necesitan estar aplicadas en orden de aumentar la prioridad. Algunos algoritmos de ordenamiento agrupados segn estabilidad tomando en cuenta la complejidad computacional.
Estables Nombre traducido Ordenamiento burbuja de Nombre original Bubblesort Complejidad Memoria Mtodo

O(n)

O(1)

Intercambio

Ordenamiento insercin Ordenamiento mezcla Inestables Nombre traducido Ordenamiento seleccin

por

Insertion sort

O(n)

O(1)

Insercin

por

Merge sort

O(n log n)

O(n)

Mezcla

Nombre original por Selection sort

Complejidad

Memoria

Mtodo

O(n) Promedio: O(n log n), peor caso: O(n) O(n1.25)

O(1)

Seleccin

Ordenamiento rpido

Quicksort

O(log n)

Particin

Ordenamiento Shell

Shell sort

O(1)

Insercin

Pilas, Colas, Colas de Prioridad y Listas. Las pilas, colas y colas de prioridad simplifican ciertos operaciones de programacin. En estas estructuras slo un item de datos puede ser accesado

Pilas
o o

Permite el acceso al ltimo elemento insertado Las principales operaciones son: push (insertar) un item en el tope de la pila y pop (remover) un elemento del tope de la pila

Colas
o o

Permite el acceso al primer elemento insertado Las operaciones son: insertar un elemento al final y remover un elemento del frente Una cola puede ser implementada como una cola circular, la cual es basada en arreglos cuyos ndices dan vuelta al pasarse de los lmites del arreglo

Colas de Prioridad
o o

Permite el acceso al menor elemento Las operaciones importante son: insertar ordenadamente un elemento y remover el menor elemento

Estas estructuras de datos pueden ser implementadas con arreglos u otro mecanismo como listas enlazadas. Ins Arr. No ordenados O(1) Arr. Lista Pilas Colas Ordenados O(N) O(1) O(1) O(1) Bus O(N) O(log n) O(N) O(N) O(N) Eli O(N) O(1) O(1) O(1) O(1)

Recursin - Se llama a si mismmo. - Cuando se llama a si mismo es para resolver un problema ms pequeo. - Debe existir alguna versin del problema que se pueda resolver sin llamarse a si mismo.