Está en la página 1de 8

Universidad Central del Ecuador

Algoritmos

Título: Montículos y Programación Dinámica

Julián Rosero
Cristian Pujota
José Pillajo
Algoritmos

MONTÍCULOS
Un montículo es un árbol binario en el que los valores de cada nodo son menores o igual que los valores de sus
hijos, es decir ningún padre tiene un hijo mayor o menor a él. Ejemplo:

56

37 48
Universidad Central del Ecuador

5 7 14
Como observamos el ejemplo es un montículo binario ya que ningún nodo padre tiene un hijo con un valor
mayor que él.

El orden de los montículos puede ser:

 min-heap : el valor de cada nodo es mayor o igual que el valor de su padre, con el elemento de
valor mínimo en la raíz.
 max-heap : el valor de cada nodo es menor o igual que el valor de su padre, con el elemento de
valor máximo en la raíz.

En el caso del ejemplo se trataría de un montículo max heap.

- USOS DE MONTÍCULOS

2
Algoritmos
Una estructura de montículo tiene aplicaciones importantes en otros algoritmos conocidos:

Algoritmos de ordenación como Heapsort, uno de los algoritmos más relevantes

con un coste eficiente en el caso peor.

Algoritmos de selección: la búsqueda de mínimos, máximos, medianas o el késimo

mayor elemento pueden realizarse en tiempo lineal mediante el uso de montículos.

Algoritmos basados en grafos: usando montículos como estructuras internas de

almacenamiento de nodos o aristas, la complejidad puede reducirse en un orden

polinómico.

- IMPLEMENTACIÓN DE MONTÍCULOS

La estructura de datos para implementar el montículo consta de un registro con tres

elementos:

1) Un vector T[1..n].
2) Un contador e para el número de elementos del montículo.
3) Un valor para el tamaño máximo del montículo.

Universidad Central del Ecuador


El montículo tiene las operaciones básicas siguientes:

CreaMonticuloVacio: devuelve un montículo vacío.

MonticuloVacio: devuelve cierto si el montículo está vacío.

Flotar: reubica el elemento i-esimo del vector en caso de que este sea mayor que el

padre (montículo de máximos).

Hundir: reubica el elemento i-esimo del vector en caso de que éste sea menor que alguno de sus hijos.
En tal caso, intercambia su valor por el del mayor de sus hijos (montículo de máximos).

Insertar: inserta un elemento en el montículo y lo flota hasta restaurar la propiedad de

montículo.

Primero: devuelve la cima del montículo sin modificarlo.

ObtenerCima: devuelve la cima del montículo, la elimina del mismo y recompone la

propiedad de montículo. (Implementación y usos tomados de:https://aprendeyprogramablog.wordpress.com).

- EJEMPLO DE MONTÍCULO EN JAVA

2
Algoritmos
Aplicaremos el algoritmo de ordenamiento por montículo el cual recorre el conjunto de elementos
desde la posición de la mitad hasta la primera organizando el montículo correspondiente a dicho
elemento. Una vez terminado este proceso, se inicia el proceso de ordenación intercambiando el primer
elemento por el último del arreglo y reorganizando el montículo a partir de la primera posición.

// Ordenacion por monticulos - HeapSort

public static void ordenacionMonticulos(int[] v) {

final int N = v.length;

for(int nodo = N/2; nodo>=0; nodo--) hacerMonticulo(v, nodo, N-1);

for(int nodo = N-1; nodo>=0; nodo--) {

int tmp = v[0];

v[0] = v[nodo];

v[nodo] = tmp;

hacerMonticulo(v, 0, nodo-1);

}
Universidad Central del Ecuador
public static void hacerMonticulo(int[] v, int nodo, int fin) {

int izq = 2*nodo+1;

int der = izq+1;

int may;

if(izq>fin) return;

if(der>fin) may=izq;

else may= v[izq]>v[der]?izq:der;

if(v[nodo] < v[may]) {

int tmp = v[nodo];

v[nodo] = v[may];

v[may] = tmp;

hacerMonticulo(v, may, fin);

2
Algoritmos
La complejidad del algoritmo de ordenación por montículos es O(n log n) teniendo en cuenta que el
proceso de organizar el montículo en el peor caso solamente tiene que hacer intercambios sobre una sola
línea de elementos desde la raíz del árbol hasta alguna de las hojas para un máximo de log n intercambios.

( Ejemplo tomado de : http://jorgep.blogspot.com/2010/09/ordenacion-por-monticulos-heap-sort.html)

PROGRAMACIÓN DINÁMICA
La programación dinámica es la técnica de diseño más potente para resolver problemas de optimización.

La programación dinámica se utiliza cuando los subproblemas no son independientes, por ejemplo, cuando
comparten los mismos subproblemas. En este caso, el algoritmo divide y vencerás puede hacer más trabajo
del necesario, ya que resuelve el mismo subproblema varias veces.

Para un mejor entendimiento del problema tenemos el ejemplo del algoritmo de Fibonacci tratado con el
método divide y vencerás.

Universidad Central del EcuadorFib(3)

Fib(2) Fib(1)

Fib(1) Fib(0) Fib(0)


Como observamos en el ejemplo, si realizamos el algoritmo divide y vencerás éste realizara varios procesos
innecesarios repetidos, aquí podemos mirar claramente los subprocesos iguales y es aquí donde entra la
programación dinámica para poder optimizar dichos subprocesos innecesarios.

- ¿CUÁNDO FUNCIONA?

2
Algoritmos
La programación dinámica
funciona cuando un problema
tiene las siguientes
características:
Subestructura óptima: si una
solución óptima contiene sub
soluciones óptimas, entonces
un problema exhibe una
subestructura óptima.

Subp
(Imagen tomada de: “ https://www.ifsworld.com”)
roblemas superpuestos: cuando un algoritmo recursivo
visitaría los mismos subproblemas repetidamente, entonces
un problema tiene subproblemas superpuestos.

Si un problema tiene una subestructura óptima, entonces podemos definir recursivamente una
solución óptima. Si un problema tiene subproblemas que se superponen, entonces podemos
mejorar una implementación recursiva calculando cada subproblema solo una vez.
Si un problema no tiene una subestructura óptima, no hay una base para definir un algoritmo
Universidad Central del Ecuador
recursivo para encontrar las soluciones óptimas. Si un problema no tiene subproblemas
superpuestos, no tenemos nada que ganar al usar la programación dinámica.
Si el espacio de los subproblemas es suficiente (es decir, polinomio en el tamaño de la entrada), la
programación dinámica puede ser mucho más eficiente que la recursión.

- EJEMPLO EN JAVA

La intuición detrás de la programación dinámica es que cambiamos espacio por tiempo, es decir,
para decir que en lugar de calcular todos los estados que toman mucho tiempo, pero sin espacio,
tomamos espacio para almacenar los resultados de todos los subproblemas para ahorrar tiempo
más tarde.

Intentemos entender esto tomando un ejemplo de los números de Fibonacci.

Fibonacci (n) = 1; si n = 0
Fibonacci (n) = 1; si n = 1
Fibonacci (n) = Fibonacci (n-1) + Fibonacci (n-2)

2
Algoritmos
Entonces, los primeros números de esta serie serán: 1, 1, 2, 3, 5, 8, 13, 21 ... ¡ y así sucesivamente!

Un código para ello usando pura recursión:

int fib (int n) {


if (n < 2)
return 1;
return fib(n-1) + fib(n-2);
}

Usando
el

void fib () {
fibresult[0] = 1;
fibresult[1] = 1;
for (int i = 2; i<n; i++)
fibresult[i] = fibresult[i-1] + fibresult[i-2];
}

enfoque de Programación Dinámica con memorización:

Universidad Central del Ecuador


En el código recursivo, muchos valores se recalculan varias veces, en cambio en el código dinámico
se calcula una única vez.

(Ejemplo tomado de: “ https://www.hackerearth.com/practice/algorithms/dynamic-programming” )

- CONCLUSIÓN
La programación dinámica es una herramienta que nos puede ahorrar una gran cantidad de tiempo
de cómputo a cambio de una mayor complejidad de espacio, ya que algunos de ellos solo van a la
mitad (se necesita una matriz para la memorización, pero se utiliza una matriz en constante cambio).

Esto depende en gran medida del tipo de sistema en el que esté trabajando; si el tiempo de CPU es
valioso, opta por una solución que consuma memoria; por otra parte, si su memoria es limitada, opta
por una solución más lenta para una mejor relación tiempo / espacio de complejidad.

2
Algoritmos

Universidad Central del Ecuador

También podría gustarte