Diccionario de prioridad C ++

Necesito un contenedor para almacenar pares, tengo dos operaciones:

  1. actualizar valor por clave
  2. obtenga la clave con el valor máximo.

Para la primera operación, map es una buena estructura. Para la segunda operación, parece que la cola de prioridad es buena. ¿Cómo harías esto? ¿Hay alguna forma de realizar ambas operaciones sin tener un bucle O (n)? Gracias.

preguntado el 30 de agosto de 11 a las 22:08

5 Respuestas

Una solución asintóticamente eficiente para esto sería usar una combinación de una tabla hash y un montón de Fibonacci. Usaría la tabla hash para poder acceder al valor asociado con cualquier clave en particular en el tiempo O (1), y usaría el montón de Fibonacci para poder buscar rápidamente el par clave / valor con el valor más bajo (al hacerlo en O (1)).

Si desea cambiar el valor asociado con una clave, si está aumentando el valor, puede hacerlo en el tiempo O (1) (amortizado) utilizando la operación de tecla de aumento en el montón de Fibonacci, que tiene O (1) tiempo amortizado. Si está disminuyendo el valor, tomará O (log n) tiempo para eliminar el elemento del montón de Fibonacci y luego reinsertarlo.

En general, esto da una complejidad de tiempo de

  • Inserte un nuevo elemento: O (1) para hash, O (1) para insertar en el montón de Fibonacci: O (1) tiempo.
  • Eliminar un elemento: O (1) para hash, O (log n) para eliminar del montón de Fibonacci: Tiempo O (log n).
  • Encuentre el elemento superior: O (1) para buscar en el montón de Fibonacci: O (1) tiempo.
  • Aumentar un valor: O (1) para hash, O (1) para aumentar clave: O (1) tiempo.
  • Disminuir un valor: O (1) para hash, O (log n) para eliminar / insertar: Tiempo O (log n).

¡Espero que esto ayude!

Respondido 31 ago 11, 03:08

¡Esta es una buena solución! Sin embargo, también debe tener en cuenta las desventajas: mantenerlos sincronizados y el uso de la memoria. - Pato morando

@Mooing Duck: idealmente, terminarías esto en su propia clase para que no puedas desincronizar los dos entre sí. Y sí, hay más uso de memoria, aunque es solo un factor constante más que tener solo una de las dos estructuras. - templatetypedef

@templatetypedef: +1, pero un montón de Fibonacci en lugar de un montón binario (o nary-heap), ¿en serio? Entiendo que los límites amortizados teóricos pueden ser mejores, pero ¿es este el caso en la práctica? Ver, por ejemplo: stackoverflow.com/questions/504823/…. Además, ¿no tienen realmente los montones de Fibonaaci una complejidad pobre en el peor de los casos para algunas operaciones? - Darren Engwirda

@Darren Engwirda- Los montones de Fibonacci tienen un mal desempeño en el peor de los casos, pero un excelente desempeño amortizado. Si debe garantizar que cada operación sea rápida, también funcionaría una cola Brodal. Y sí, los montones de Fibonacci pueden ser más lentos que los montones binarios. Esta solucion es asintóticamente bueno, pero puede que no sea tan rápido en la práctica a menos que el conjunto de datos sea bastante grande. - templatetypedef

@templatetypedef: LOL a factor constante más de una estructura, por algo asintóticamente bueno en lugar de prácticamente bueno. - Pato morando

Crear un estructura del montón (para la segunda viñeta) y coloque cada uno de sus nodos en un mapa (para la primera viñeta).

EDIT: Una implementación de min heap que había hecho en el pasado

#ifndef MINHEAP_H
#define MINHEAP_H

//////////////////////// MinHeap with Map for Data ////////////////////////

template <class T, class M = int> class MinHeap {
    T*          array;
    unsigned const int  arr_max;
    unsigned int        elements;
    M           map;

    void percolate_down(unsigned int i=0) {
        unsigned int n = elements-1, min;
        do {
            unsigned int l = 2*i + 1, r = 2*i + 2;
            if (l <= n && array[i] > array[l]) min = l;
            else min = i;
            if (r <= n && array[i] > array[r] && array[l] > array[r]) min = r;
            if (i != min) {
                T temp = array[i];
                array[i] = array[min];
                array[min] = temp;
                map.update(array[i], i);
                map.update(array[min], min);
                i = min;
            } else break;
        } while (i < n);
    }
    void percolate_up(unsigned int i) {
        while (i && array[(i-1)/2] > array[i]) {
            T temp = array[i];
            array[i] = array[(i-1)/2];
            array[(i-1)/2] = temp;
            map.update(array[i], i);
            map.update(array[(i-1)/2], (i-1)/2);
            i = (i-1)/2;
        }
    }
public:
    MinHeap(const int max) : array(new T[max]), arr_max(max), elements(0), map(max) {}
    ~MinHeap(void) { delete[] array; }

    bool empty(void) const { return elements == 0; }
    unsigned int capacity(void) const { return arr_max; }
    unsigned int size(void) const { return elements; }
    const M& heapmap(void) const { return map; }
    const T& peek(unsigned int i=0) const { return array[i]; }

    bool insert(T& element) {
        if (arr_max == elements) return false;

        unsigned int k = elements++;
        map.update(element, k);
        array[k] = element;
        percolate_up(k);
        return true;
    }
    unsigned int mass_insert(T copy[], unsigned int n) {
        unsigned int i = 0;     
        for( ; i < n ; i++) if (!insert(copy[i])) break;
        return i;
    }
    bool delete_min(void) {
        if (elements == 0) return false;

        map.update(array[0], arr_max+1);
        array[0] = array[--elements];
        map.update(array[0], 0);
        percolate_down();
        return true;
    }
    bool delete_element(unsigned int i) {
        if (i > elements) return false;

        map.update(array[i], arr_max+1);
        T temp = array[i];      
        array[i] = array[--elements];
        map.update(array[i], i);
        if (array[i] > temp) percolate_down(i);
        else if (temp > array[i]) percolate_up(i);
        return true;
    }
    bool update(unsigned int i, T& element) {
        if (i > elements) return false;

        map.update(array[i], arr_max+1);
        T temp = array[i];      
        array[i] = element;
        map.update(array[i], i);
        if (array[i] > temp) percolate_down(i);
        else if (temp > array[i]) percolate_up(i);
        return true;
    }

//  void print() { using namespace std; for (unsigned int i=0 ; i < elements ; i++) cout << array[i] << " | "; cout << endl; }


    // Iterators
/*
    friend class Const_Iterator;

    class Const_Iterator {
        MinHeap<T>* heap;
        unsigned int    index;
        Const_Iterator(MinHeap<T>* h, unsigned int i) : heap(h), index(i) {}
    public:
        Const_Iterator(const Const_Iterator& clone) : heap(clone.heap), index(clone.index) {}

        friend Const_Iterator MinHeap<T>::begin(void);
    };

    Const_Iterator begin(void) { return Const_Iterator(this, 0); }
*/
};

//////////////////////////////////////////////////////////////////////////////


//////////////////////// MinHeap without Map for Data ////////////////////////

template <class T> class MinHeap <T, int> {
    T*          array;
    unsigned const int  arr_max;
    unsigned int        elements;

    void percolate_down(unsigned int i=0) {
        unsigned int n = elements-1, min;
        do {
            unsigned int l = 2*i + 1, r = 2*i + 2;
            if (l <= n && array[i] > array[l]) min = l;
            else min = i;
            if (r <= n && array[i] > array[r] && array[l] > array[r]) min = r;
            if (i != min) {
                T temp = array[i];
                array[i] = array[min];
                array[min] = temp;
                i = min;
            } else break;
        } while (i < n);
    }
    void percolate_up(unsigned int i) {
        while (i && array[(i-1)/2] > array[i]) {
            T temp = array[i];
            array[i] = array[(i-1)/2];
            array[(i-1)/2] = temp;
            i = (i-1)/2;
        }
    }
public:
    MinHeap(const int max) : array(new T[max]), arr_max(max), elements(0) {}
    ~MinHeap(void) { delete[] array; }

    bool empty(void) const { return elements == 0; }
    unsigned int capacity(void) const { return arr_max; }
    unsigned int size(void) const { return elements; }
    const T& peek(unsigned int i=0) const { return array[i]; }

    bool insert(T& element) {
        if (arr_max == elements) return false;

        unsigned int k = elements++;
        array[k] = element;
        percolate_up(k);
        return true;
    }
    unsigned int mass_insert(T copy[], unsigned int n) {
        unsigned int i = 0;     
        for( ; i < n ; i++) if (!insert(copy[i])) break;
        return i;
    }
    bool delete_min(void) {
        if (elements == 0) return false;

        array[0] = array[--elements];
        percolate_down();
        return true;
    }
    bool delete_element(unsigned int i) {
        if (i > elements) return false;

        T temp = array[i];      
        array[i] = array[--elements];
        if (array[i] > temp) percolate_down(i);
        else if (temp > array[i]) percolate_up(i);
        return true;
    }
    bool update(unsigned int i, T& element) {
        if (i > elements) return false;

        T temp = array[i];      
        array[i] = element;
        if (array[i] > temp) percolate_down(i);
        else if (temp > array[i]) percolate_up(i);
        return true;
    }

//  void print() { using namespace std; for (unsigned int i=0 ; i < elements ; i++) cout << array[i] << " | "; cout << endl; }
};

//////////////////////////////////////////////////////////////////////////////

#endif // MINHEAP_H

Respondido 31 ago 11, 02:08

También podrías usar std::priority_queue. - Dawson

@Toolbox Una estructura de montón es una forma de implementar un priority_queue :) - George Kastrinis

Lo sé, lo que quise decir es que no hay necesidad de volver a implementar lo que ya es parte de C ++ estándar. - Dawson

boost :: bimap podría hacer lo que quieras, donde el mapa inverso se usa para implementar # 2.

Respondido 31 ago 11, 02:08

¿Qué pasa si varias claves tienen el mismo valor? - templatetypedef

@templatetypedef: su elección, varios índices son posibles, como bimap<int, multiset_of<std::string> >. - Matthieu M.

Según mi estándar C ++ 0x, The fundamental property of iterators of associative containers is that they iterate through the containers in the non-descending order of keys where non-descending is defined by the comparison that was used to construct them.

Así que usa un mapa. La búsqueda aleatoria es O (log (n)), y obtener el elemento más alto toma O (1) tiempo.

value getHighest(map<key, value>& map) {
    assert(map.empty() == false);
    return (--map.end())->second;
}

Respondido 31 ago 11, 03:08

¿Esto es válido para el mapa hash? entonces la búsqueda también sería O (1) - user875367

No, los hash están desordenados, por lo que no puede encontrar el elemento más alto. - Pato morando

Creo que el bimapa es la mejor ruta

Respondido 31 ago 11, 03:08

¿Qué sucede si varias claves tienen el mismo valor asociado? - templatetypedef

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.