VI Colas de prioridad



Documentos relacionados
Algorítmica: Heaps y heapsort

Ampliación de Estructuras de Datos

Programación de Sistemas

Árboles. Cursos Propedéuticos Dr. René Cumplido M. en C. Luis Rodríguez Flores

Estructuras de datos: Árboles binarios de

Árboles AVL. Laboratorio de Programación II

3. COLA DE PRIORIDAD DEFINICION (I)

En cualquier caso, tampoco es demasiado importante el significado de la "B", si es que lo tiene, lo interesante realmente es el algoritmo.

Estructuras de Datos. Montículos. Montículos. Montículos. Tema 3. Montículos. Definiciones básicas: Definiciones básicas:

Tema 10- Representación Jerárquica: Tema 10- Representación Jerárquica: Árboles Binarios

DEFINICION. Ing. M.Sc. Fulbia Torres Asignatura: Estructuras de Datos Barquisimeto 2006

Capítulo 6. ÁRBOLES.

Capítulo 12: Indexación y asociación

Tecnólogo Informático- Estructuras de Datos y Algoritmos- 2009

NIVEL 15: ESTRUCTURAS RECURSIVAS BINARIAS

Árboles balanceados. Alonso Ramírez Manzanares Computación y Algoritmos Thursday, April 30, 15

Clase 32: Árbol balanceado AVL

Laboratorio 7 Motor de búsqueda web basado en el TAD Árbol Binario de Búsqueda GUIÓN DEL LABORATORIO

árbol como un conjunto de nodos y líneas

Solución al Examen de Prácticas de Programación (Ingeniería Informática)

Arboles Binarios de Búsqueda

Árboles binarios de búsqueda ( BST )

UNIVERSIDAD NACIONAL AUTÓNOMA DE MÉXICO. Introducción FACULTAD DE INGENIERÍA. Ordenación

Árboles Binarios Ordenados Árboles AVL

Resumen de técnicas para resolver problemas de programación entera Martes, 9 de abril. Enumeración. Un árbol de enumeración

Algoritmos sobre Grafos


EXAMEN FINAL Metodología y Programación Orientada a Objetos. Curso Cuatrimestre de otoño. 17 de Enero de 2011

Árboles binarios de búsqueda

Analisis de algoritmos

Análisis amortizado El plan:

a < b y se lee "a es menor que b" (desigualdad estricta) a > b y se lee "a es mayor que b" (desigualdad estricta)

ARREGLOS DEFINICION GENERAL DE ARREGLO

Introducción a la Teoría de Grafos

FUNDAMENTOS DE PROGRAMACIÓN. SEPTIEMBRE 2005

8. Sentencia return y métodos

Notas de Clase. Prof. Juan Andrés Colmenares, M.Sc. Instituto de Cálculo Aplicado Facultad de Ingeniería Universidad del Zulia. 21 de febrero de 2004

Árbol binario. Elaborado por Ricardo Cárdenas cruz Jeremías Martínez Guadarrama Que es un árbol Introducción

Programación Genética

ARBOLES ARBOLES BINARIOS ORDENADOS. REPRESENTACIÓN Y OPERACIONES

Trabajo Práctico N 4: Diseño por Contratos

Centro de Capacitación en Informática

Escuela Politécnica Superior de Ingeniería Departamento de Ingeniería Informática

Profesorado de Informática Ciencias de la Computación INET- DFPD Matemática I - Matemática Discreta usando el computador Ing. Prof.

Operaciones Morfológicas en Imágenes Binarias

PRÁCTICA No. 13 ÁRBOL BINARIO DE BÚSQUEDA

Formularios. Formularios Diapositiva 1

Registro (record): es la unidad básica de acceso y manipulación de la base de datos.

Estructura de datos y de la información Boletín de problemas - Tema 7

Tema 3. Espacios vectoriales

Metodología de la Programación II. Recursividad

Primer Parcial de Programación 3 (1/10/2009)

Java Inicial (20 horas)

2.2 Transformada de Laplace y Transformada Definiciones Transformada de Laplace

La nueva criba de Eratóstenes Efraín Soto Apolinar 1 F.I.M.E. U.A.N.L. San Nicolás, N.L. México. efrain@yalma.fime.uanl.mx

1. Ejemplo de clase : La clase Cuenta 2. Uso de la clase Cuenta. 3. Métodos y objetos receptores de mensajes (Importante)

MÉTODOS DE ELIMINACIÓN Son tres los métodos de eliminación más utilizados: Método de igualación, de sustitución y de suma o resta.

Nota 2. Luis Sierra. Marzo del 2010

Tipos Abstractos de Datos

Práctica 2: Simón dice

Bases de datos en Excel

Tema 2. Espacios Vectoriales Introducción

EDA. Tema 8 Colas de Prioridad: Heaps

Sistemas de amortización

Definición Dados dos números naturales m y n, una matriz de orden o dimensión m n es una tabla numérica rectangular con m filas y n columnas.

REDES DE ÁREA LOCAL. APLICACIONES Y SERVICIOS EN WINDOWS

Divisibilidad y números primos

Problemas indecidibles

ESTRUCTURA DE DATOS: ARREGLOS

PESTAÑA DATOS - TABLAS EN EXCEL

Diagrama de Gantt en Excel

ANÁLISIS SEMÁNTICO. Especificación formal: Semántica Operacional, semántica denotacional, semántica Axiomática, Gramáticas con Atributos.

Definición de clases: Herencia, polimorfismo, ligadura dinámica

Subespacios vectoriales en R n

CASO PRÁCTICO DISTRIBUCIÓN DE COSTES

Examen escrito de Programación 1

Redes de área local: Aplicaciones y servicios WINDOWS

ARBOLES ARBOLES BINARIOS ORDENADOS. REPRESENTACIÓN Y OPERACIONES

Instituto de Computación - Facultad de Ingeniería - Universidad de la República

INSTITUTO UNIVERSITARIO DE TECNOLOGÍA JOSE LEONARDO CHIRINO PUNTO FIJO EDO-FALCON CATEDRA: ARQUITECTURA DEL COMPUTADOR PROFESOR: ING.

11. Algunas clases estándar de Java (II)

ANÁLISIS DE DATOS NO NUMERICOS

Patrones para persistencia (I) Ingeniería del Software II

Clases y funciones amigas: friend

Operación Microsoft Access 97

Los números racionales

DESCRIPCIÓN DE LA METODOLOGÍA UTILIZADA EN EL PROGRAMA DE CESTAS REDUCIDAS ÓPTIMAS

1. (2 puntos) En la V Caminata Madrileño Manchega, los participantes caminan de Madrid

INSTITUTO POLITÉCNICO NACIONAL ESCUELA SUPERIOR DE INGENIERÍA MECÁNICA Y ELÉCTRICA UNIDAD CULHUACÁN INTEGRANTES

Sistemas de numeración

BREVE MANUAL DE SOLVER

1. DML. Las subconsultas

INTRODUCCION A LA PROGRAMACION DE PLC

Universidad Católica del Maule. Fundamentos de Computación Especificación de tipos de datos ESPECIFICACIÓN ALGEBRAICA DE TIPOS DE DATOS

Búsqueda heurística Prof. Constantino Malagón

GRAFOS. Prof. Ing. M.Sc. Fulbia Torres

33 El interés compuesto y la amortización de préstamos.

Estructuras de Datos y Algoritmos. Árboles de Expresión

Estas visiones de la información, denominadas vistas, se pueden identificar de varias formas.

Transcripción:

VI Colas de prioridad

Una cola de prioridad (cat: cua de prioritat; ing: priority queue) es una colección de elementos donde cada elemento tiene asociado un valor susceptible de ordenación denominado prioridad. Una cola de prioridad se caracteriza por admitir inserciones de nuevos elementos y la consulta y eliminación del elemento de mínima prioridad. Análogamente se pueden definir colas de prioridad que admitan la consulta y eliminación del elemento de máxima prioridad en la colección. Colas de prioridad 323

En esta especificación asumimos que el tipo Prio ofrece una relación de orden total <. Por otra parte, puede darse el caso de que existan varios elementos con igual prioridad y en dicho caso es irrelevante cuál de los elementos es devuelto por min o eliminado por elim min. En ocasiones se utiliza una operación prio min que devuelve la mínima prioridad. Colas de prioridad 324

template <typename Elem, typename Prio> class ColaPrioridad { public: // Constructora, crea una cola vacía. ColaPrioridad() throw( error); // Destructora, constr. por copia y asignación ~ColaPrioridad() throw(); ColaPrioridad(const ColaPrioridad& P) throw(error); ColaPrioridad& operator =( const ColaPrioridad P) throw( error); // A~nade el elemento x con prioridad p a la cola de // prioridad. void inserta(cons Elem& x, const Prio& p) throw(error) // Devuelve un elemento de mínima prioridad en la cola de // prioridad. Se lanza un error si la cola está vacía. Elem min() const throw(error); // Devuelve la mínima prioridad presente en la cola de // prioridad. Se lanza un error si la cola está vacía. Prio prio_min() const throw(error); // Elimina un elemento de mínima prioridad de la cola de // prioridad. Se lanza un error si la cola está vacía. void elim_min() throw(error); // Devuelve cierto si y sólo si la cola está vacía. bool vacia() const throw(); ; Colas de prioridad 325

Las colas de prioridad tienen múltiples usos. Con frecuencia se emplean para implementar algoritmos voraces. Este tipo de algoritmos suele tener una iteración principal, y una de las tareas a realizar en cada una de dichas iteraciones es seleccionar un elemento de entre varios que minimiza (o maximiza) un cierto criterio de optimalidad local. El conjunto de elementos entre los que se ha de efectuar la selección es frecuentemente dinámico y admitir inserciones eficientes. Algunos de estos algoritmos incluyen: Algoritmos de Kruskal y Prim para el cálculo del árbol de expansión mínimo de un grafo etiquetado. Algoritmo de Dijkstra para el cálculo de caminos mínimos en un grafo etiquetado. Construcción de códigos de Huffman (códigos binarios de longitud media mínima). Otra tarea para la que obviamente podemos usar una cola de prioridad es para ordenar. Colas de prioridad 326

// Tenemos dos arrays Peso y Simb con los pesos atómicos // y símbolos de n elementos químicos, // p.e., Simb[i] = "C" y Peso[i] = 12.2. // Utilizamos una cola de prioridad para ordenar la // información de menor a mayor símbolo en orden alfabético ColaPrioridad<double, string> P; for (int i = 0; i < n; ++i) P.inserta(Peso[i], Simb[i]); int i = 0; while(! P.vacia()) { Peso[i] = P.min(); Simb[i] = P.prio_min(); ++i; P.elim_min(); También se usan colas de prioridad para hallar el k-ésimo elemento de un vector no ordenado. Se colocan los k primeros elementos del vector en una max-cola de prioridad y a continuación se recorre el resto del vector, actualizando la cola de prioridad cada vez que el elemento es menor que el mayor de los elementos de la cola, eliminando al máximo e insertando el elemento en curso. Colas de prioridad 327

La mayoría de técnicas empleadas en la implementación de diccionarios puede usarse para implementar colas de prioridad. La excepción la constituyen las tablas de dispersión y los tries. Se puede usar una lista ordenada por prioridad. Tanto la consulta como la eliminación del mínimo son triviales y su coste es Θ(1). Pero las inserciones tienen coste lineal, tanto en caso peor como en promedio. Otra opción es usar un árbol de búsqueda (equilibrado o no), utilizando como criterio de orden de sus elementos las correspondientes prioridades. Hay que modificar ligeramente el invariante de representación para admitir y tratar adecuadamente las prioridades repetidas. Puede garantizarse en tal caso que todas las operaciones (inserciones, consultas, eliminaciones) tienen coste Θ(logn): en caso peor si el BST es equilibrado (AVL), y en caso medio si no lo es. Colas de prioridad 328

También pueden usarse skip lists con idéntico rendimiento al que ofrecen los BSTs (aunque los costes son logarítmicos sólo en caso medio, dicho caso medio no depende ni del orden de inserción ni de la existencia de pocas prioridades repetidas). Si el conjunto de posibles prioridades es reducido entonces será conveniente emplear una tabla de listas, correspondiendo cada lista a una prioridad o intervalo reducido de prioridades. En lo que resta estudiaremos una técnica específica para la implementación de colas de prioridad basada en los denominados montículos. Colas de prioridad 329

Un montículo (ing: heap) es un árbol binario tal que 1. todos las hojas (subárboles son vacíos) se sitúan en los dos últimos niveles del árbol. 2. en el antepenúltimo nivel existe a lo sumo un nodo interno con un sólo hijo, que será su hijo izquierdo, y todos los nodos a su derecha en el mismo nivel son nodos internos sin hijos. 3. el elemento (su prioridad) almacenado en un nodo cualquiera es mayor (menor) o igual que los elementos almacenados en sus hijos izquierdo y derecho. Se dice que un montículo es un árbol binario quasi-completo debido a las propiedades 1-2. La propiedad 3 se denomina orden de montículo, y se habla de max-heaps o min-heaps según que los elementos sean ó ques sus hijos. En lo sucesivo sólo consideraremos max-heaps. Colas de prioridad 330

n = 10 76 nivel 0 altura = 4 72 34 nivel 1 59 63 17 29 nivel 2 37 33 29 nivel 3 nivel 4 hojas Colas de prioridad 331

De las propiedades 1-3 se desprenden dos consecuencias importantes: 1. El elemento máximo se encuentra en la raíz. 2. Un heap de n elementos tiene altura h = log 2 (n+1). La consulta del máximo es sencilla y eficiente pues basta examinar la raíz. Colas de prioridad 332

Cómo eliminar el máximo? Un procedimiento que se emplea a menudo consiste en ubicar al último elemento del montículo (el del último nivel más a la derecha) en la raíz, sustituyendo al máximo; ello garantiza que se preservan las propiedades 1-2. Pero como la propiedad 3 deja eventualmente de satisfacerse, debe reestablecerse el invariante para lo cual se emplea un procedimiento privado denominado hundir. Éste consiste en intercambiar un nodo con el mayor de sus dos hijos si el nodo es menor que alguno de ellos, y repetir este paso hasta que el invariante se haya reestablecido. Colas de prioridad 333

32 72 34 59 63 17 29 37 33 29 72 32 34 59 63 17 29 37 33 29 72 63 34 59 32 17 29 37 33 29 Colas de prioridad 334

Cómo añadir un nuevo elemento? Una posibilidad consiste en colocar el nuevo elemento como último elemento del montículo, justo a la derecha del último o como primero de un nuevo nivel. Para ello hay que localizar al padre de la primera hoja y sustituirla por un nuevo nodo con el elemento a insertar. A continuación hay que reestablecer el orden de montıculo empleando para ello un procedimiento flotar, que trabaja de manera similar pero a la inversa de hundir: el nodo en curso se compara con su nodo padre y se realiza el intercambio si éste es mayor que el padre, iterando este paso mientras sea necesario. Colas de prioridad 335

Puesto que la altura del heap es Θ(logn) el coste de inserciones y eliminaciones es O(logn). Se puede implementar un heap mediante memoria dinámica. La representación elegida debe incluir apuntadores al hijo izquierdo y derecho y también al padre, y resolver de manera eficaz la localización del último elemento y del padre de la primera hoja. Una alternativa atractiva es la implementación de heaps mediante un vector. No se desperdicia demasiado espacio ya que el heap es quasi-completo. Las reglas para representar los elementos del heap en un vector son simples: 1. A[1] contiene la raíz. 2. Si 2i n entonces A[2i] contiene al hijo izquierdo del elemento en A[i] y si 2i + 1 n entonces A[2i+1] contiene al hijo derecho de A[i]. 3. Si idiv 2 1 entonces A[idiv 2] contiene al padre de A[i]. Colas de prioridad 336

Nótese que las reglas anteriores implican que los elementos del montículo se ubican en posiciones consecutivas del vector, colocando la raíz en la primera posición y recorriendo el árbol por niveles, de izquierda a derecha. template <typename Elem, typename Prio> class ColaPrioridad { public:... private: static const int MAX_ELEM =...; int nelems; // número de elementos en el heap // array de MAX_ELEMS pares <Elem, Prio>, // la componente 0 no se usa pair<elem, Prio> h[max_elem + 1]; ; void flotar(int j) throw(); void hundir(int j) throw(); Colas de prioridad 337

template <typename Elem, typename Prio> bool ColaPrioridad<Elem,Prio>::vacia() const throw() { return nelems == 0; template <typename Elem, typename Prio> void ColaPrioridad<Elem,Prio>::inserta(cons Elem& x, cons Prio& p) throw(error) { if (nelems == MAX_ELEMS) throw error(colallena); ++nelems; h[nelems] = make_pair(x, p); flotar(nelems); template <typename Elem, typename Prio> Elem ColaPrioridad<Elem,Prio>::min() const throw(error) { if (nelems == 0) throw error(colavacia); return h[1].first; template <typename Elem, typename Prio> Prio ColaPrioridad<Elem,Prio>::prio_min() const throw(error) { if (nelems == 0) throw error(colavacia); return h[1].second; template <typename Elem, typename Prio> void ColaPrioridad<Elem,Prio>::elim_min() const throw(error) { if (nelems == 0) throw error(colavacia); swap(h[1], h[nelems]); --nelems; hundir(1); Colas de prioridad 338

// Versión recursiva. // Hunde el elemento en la posición j del heap // hasta reestablecer el orden del heap; por // hipótesis los subárboles del nodo j son heaps. // Coste: O(log(n/j)) template <typename Elem, typename Prio> void ColaPrioridad<Elem,Prio>::hundir(int j) throw() { // si j no tiene hijo izquierdo, hemos terminado if (2 * j > nelems) return; int minhijo = 2 * j; if (minhijo < nelems && h[minhijo].second > h[minhijo + 1].second) ++minhijo; // minhijo apunta al hijo de minima prioridad de j // si la prioridad de j es mayor que la de su menor hijo // intercambiar y seguir hundiendo if (h[j].second > h[minhijo].second) { swap(h[j], h[minhijo]); hundir( minhijo); Colas de prioridad 339

// Versión iterativa. // Hunde el elemento en la posición j del heap // hasta reestablecer el orden del heap; por // hipótesis los subárboles del nodo j son heaps. // Coste: O(log(n/j)) template <typename Elem, typename Prio> void ColaPrioridad<Elem,Prio>::hundir(int j) throw() { bool fin = false; while (2 * j <= nelems &&!fin) { int minhijo = 2 * j; if (minhijo < nelems && h[minhijo].second > h[minhijo + 1].second) ++minhijo; if (h[j].second > h[minhijo].second) { swap(h[j], h[minhijo]); j = minhijo; else { fin = true; Colas de prioridad 340

// Flota al nodo j hasta reestablecer el orden del heap; // todos los nodos excepto el j satisfacen la propiedad // de heap // Coste: O(log j) template <typename Elem, typename Prio> void ColaPrioridad<Elem,Prio>::flotar(int j) throw() { // si j es la raíz, hemos terminado if (j == 1) return; int padre = j / 2; // si el padre tiene mayor prioridad // que j, intercambiar y seguir flotando if (h[j].second < h[padre].second) { swap(h[j], h[padre]); flotar(padre); Colas de prioridad 341

Heapsort Heapsort (Williams, 1964) ordena un vector de n elementos construyendo un heap con los n elementos y extrayéndolos, uno a uno del heap a continuación. El propio vector que almacena a los n elementos se emplea para construir el heap, de modo que heapsort actúa in-situ y sólo requiere un espacio auxiliar de memoria constante. El coste de este algoritmo es Θ(nlogn) (incluso en caso mejor) si todos los elementos son diferentes. En la práctica su coste es superior al de quicksort, ya que el factor constante multiplicativo del término nlogn es mayor. Colas de prioridad 342

// Ordena el vector v[1..n] // (v[0] no se usa) // de Elem s de menor a mayor template < typename Elem > void heapsort(elem v[], int n) { crea_max_heap(v, n); for (int i = n; i > 0; --i) { // saca el mayor elemento del heap swap(v[1], v[i]); // hunde el elemento de indice 1 // para reestablecer un max-heap en // el subvector v[1..i-1] hundir(v, i-1, 1); 1 i n A heap ordenado <_ <_ <_ <_ A[1] A[i+1] A[i+2]... A[n] A[1] = max 1 <_ k <_ i A[k] Colas de prioridad 343

76 76 72 34 59 63 17 29 37 33 29 72 34 i = 10 59 63 17 29 37 33 29 29 29 72 34 59 63 17 29 37 33 76 72 34 i = 9 59 63 17 29 37 33 72 72 63 34 59 29 17 29 37 33 76 63 34 i = 9 59 29 17 29 37 33 Colas de prioridad 344

33 33 63 34 59 29 17 29 37 72 76 63 34 i = 8 59 29 17 29 37 33 33 59 34 37 29 17 29 63 72 76 59 34 i = 7 37 29 17 29 59 59 37 34 33 29 17 29 63 72 76 i = 7 37 34 33 29 17 29 Colas de prioridad 345

// Da estructura de max-heap al // vector v[1..n] de Elem s; aquí // cada elemento se identifica con su // prioridad template < typename Elem > void crea_max_heap(elem v[], int n) { for (int i = n/2; i > 0; --i) hundir(v, n, i); Colas de prioridad 346

13 13 2 5 27 8 15 10 4 2 5 i = 8 27 8 15 10 4 satisfacen la propiedad de heap 1 13 hundir(a, 8, 4) hundir(a, 8, 3) 2 2 3 15 4 27 5 8 6 7 5 10 8 4 1 27 hundir(a, 8, 2) hundir(a, 8, 1) 2 13 3 15 4 4 5 8 6 7 5 10 8 2 Colas de prioridad 347

Sea H(n) el coste en caso peor de heapsort y B(n) el coste de crear el heap inicial. El coste en caso peor de hundir(v,i 1,1) es O(logi) y por lo tanto H(n) = B(n)+ i=n i=1 O(log i) ( ) = B(n)+O log 2 i 1 i n = B(n)+O(log(n!)) = B(n)+O(nlogn) Un análisis grueso de B(n) indica que B(n) = O(nlogn) ya que hay Θ(n) llamadas a hundir, cada una de las cuales tiene coste O(logn). Podemos concluir por tanto que H(n) = O(nlogn). No es difícil construir una entrada de tamaño n tal que H(n) = Ω(nlogn) y por tanto H(n) = Θ(nlogn) en caso peor. La demostración de que el coste de heapsort en caso mejor 3 es también Θ(nlogn) es bastante más complicada. 3 Siendo todos los elementos distintos. Colas de prioridad 348

Por otra parte, la cota dada para B(n) podemos refinarla ya que B(n) = O(log(n/i)) 1 i n/2 ) = O (log nn/2 (n/2)! ) = O (log(2e) n/2 = O(n). Puesto que B(n) = Ω(n), podemos afirmar que B(n) = Θ(n). Otra forma de demostrar que B(n) es lineal consiste en razonar del siguiente modo: Sea h = log 2 (n+1) la altura del heap. En el nivel h 1 k hay como mucho 2 h 1 k < n+1 2 k nodos y cada uno de ellos habrá de hundirse en caso peor hasta el nivel h 1; eso tiene coste O(k). Colas de prioridad 349

Por lo tanto, ya que B(n) = = O = O En general, si r < 1, 0 k h 1 ( ( n k 0 O(k) n+1 2 k k 0 k h 1 2 k k n k 0 2 k k r k = k 0 ) k 2 k = 2. ) r (1 r) 2. = O(n), Colas de prioridad 350

Aunque globalmente H(n) = Θ(n log n), es interesante el análisis detallado de B(n). Por ejemplo, utilizando un min-heap podemos hallar los k menores elementos de un vector (y en particular el k-ésimo) con coste: S(n,k) = B(n)+k O(logn) = O(n+klogn). y si k = O(n/ log n) entonces S(n, k) = O(n). Colas de prioridad 351