web-dev-qa-db-fra.com

Différence entre la file d'attente prioritaire et un tas

Il semble qu'une file d'attente prioritaire ne soit qu'un tas avec des opérations de file d'attente normales telles que l'insertion, la suppression, le sommet, etc. Est-ce la bonne façon d'interpréter une file d'attente prioritaire? Je sais que vous pouvez créer des files d'attente prioritaires de différentes manières, mais si je devais créer une file d'attente prioritaire à partir d'un segment de mémoire, est-il nécessaire de créer une classe de file d'attente prioritaire et de donner des instructions pour la création d'un segment de mémoire et les opérations de file d'attente ou n'est-il pas vraiment nécessaire de créer la classe?

Ce que je veux dire, c'est que si j'ai une fonction pour construire un tas et des fonctions pour effectuer des opérations comme insérer, supprimer, dois-je mettre toutes ces fonctions dans une classe ou puis-je simplement utiliser les instructions en les appelant dans main.

Je suppose que ma question est de savoir si avoir une collection de fonctions équivaut à les stocker dans une classe et à les utiliser via une classe ou simplement à utiliser les fonctions elles-mêmes.

Ce que j'ai ci-dessous est toutes les méthodes pour une implémentation de file d'attente prioritaire. Est-ce suffisant pour l'appeler une implémentation ou dois-je le placer dans une classe de file d'attente prioritaire désignée?

#ifndef MAX_PRIORITYQ_H
#define MAX_PRIORITYQ_H
#include <iostream>
#include <deque>
#include "print.h"
#include "random.h"

int parent(int i)
{
    return (i - 1) / 2;
}

int left(int i)
{
    if(i == 0)
        return 1;
    else
        return 2*i;
}

int right(int i)
{
    if(i == 0)
        return 2;
    else
        return 2*i + 1;
}

void max_heapify(std::deque<int> &A, int i, int heapsize)
{
    int largest;
    int l = left(i);
    int r = right(i);
    if(l <= heapsize && A[l] > A[i])
        largest = l;
    else
        largest = i;
    if(r <= heapsize && A[r] > A[largest])
        largest = r;
    if(largest != i) {
        exchange(A, i, largest);
        max_heapify(A, largest, heapsize);
        //int j = max_heapify(A, largest, heapsize);
        //return j;
    }
    //return i;
}

void build_max_heap(std::deque<int> &A)
{
    int heapsize = A.size() - 1;
    for(int i = (A.size() - 1) / 2; i >= 0; i--)
        max_heapify(A, i, heapsize);
}

int heap_maximum(std::deque<int> &A)
{
    return A[0];
}

int heap_extract_max(std::deque<int> &A, int heapsize)
{
    if(heapsize < 0)
        throw std::out_of_range("heap underflow");
    int max = A.front();
    //std::cout << "heapsize : " << heapsize << std::endl;
    A[0] = A[--heapsize];
    A.pop_back();
    max_heapify(A, 0, heapsize);
    //int i = max_heapify(A, 0, heapsize);
    //A.erase(A.begin() + i);
    return max;
}

void heap_increase_key(std::deque<int> &A, int i, int key)
{
    if(key < A[i])
        std::cerr << "New key is smaller than current key" << std::endl;
    else {
        A[i] = key;
        while(i > 1 && A[parent(i)] < A[i]) {
            exchange(A, i, parent(i));
            i = parent(i);
        }
    }
}

void max_heap_insert(std::deque<int> &A, int key)
{
    int heapsize =  A.size();
    A[heapsize] = std::numeric_limits<int>::min();
    heap_increase_key(A, heapsize, key);
}
31
Mars

Avoir une classe avec exactement l'interface dont vous avez besoin (juste insérer et pop-max?) A ses avantages.

  • Vous pouvez échanger l'implémentation (liste au lieu de tas, par exemple) ultérieurement.
  • Quelqu'un qui lit le code qui tilise la file d'attente n'a pas besoin de comprendre l'interface plus difficile de la structure de données du tas.

Je suppose que ma question est de savoir si avoir une collection de fonctions équivaut à les stocker dans une classe et à les utiliser via une classe ou simplement à utiliser les fonctions elles-mêmes.

C'est surtout équivalent si vous pensez simplement en termes de "comment se comporte mon programme". Mais ce n'est pas équivalent en termes de "comment mon programme est-il facile à comprendre par un lecteur humain"

7
Sebastian

Une file d'attente prioritaire est un type de données abstrait . C'est une façon abrégée de décrire une interface et un comportement particuliers, et ne dit rien sur l'implémentation sous-jacente.

Un tas est un structure de données . C'est un nom pour une façon particulière de stocker des données qui rend certaines opérations très efficaces.

Il se trouve qu'un tas est une très bonne structure de données pour implémenter une file d'attente prioritaire, car les opérations qui sont rendues efficaces par la structure de données du tas sont les opérations dont l'interface de file d'attente prioritaire a besoin.

93
Benjamin Lindley

Le terme file d'attente prioritaire fait référence à la structure générale des données utile pour ordonner les priorités de son élément. Il existe plusieurs façons d'y parvenir, par exemple, diverses structures arborescentes ordonnées (par exemple, un arbre splay fonctionne raisonnablement bien) ainsi que divers tas, par exemple, les tas d-tas ou les tas de Fibonacci. Conceptuellement, un tas est une structure arborescente où le poids de chaque nœud n'est pas inférieur au poids de n'importe quel nœud du sous-arbre acheminé à ce nœud.

2
Dietmar Kühl

La bibliothèque de modèles standard C++ fournit les algorithmes make_heap, Push_heap et pop_heap pour les tas (généralement implémentés en tant que tas binaires), qui fonctionnent sur des itérateurs à accès aléatoire arbitraires. Il traite les itérateurs comme une référence à un tableau et utilise la conversion de tableau en tas. Il fournit également l'adaptateur de conteneur priority_queue, qui encapsule ces fonctionnalités dans une classe de type conteneur. Cependant, il n'y a pas de prise en charge standard pour l'opération de diminution/augmentation des touches.

priorité_queue fait référence au type de données abstrait défini entièrement par les opérations qui peuvent y être effectuées. En C++ STL prioroty_queue est donc l'un des adaptateurs de séquence - adaptateurs de conteneurs de base (vector, list et deque sont basiques car ils ne peuvent pas être construits les uns sans les autres sans perte d’efficacité), défini dans <queue> entête (<bits/stl_queue.h> dans mon cas en fait). Comme le montre sa définition (comme le dit Bjarne Stroustrup):

l'adaptateur de conteneur fournit une interface restreinte à un conteneur. En particulier, les adaptateurs ne fournissent pas d'itérateurs; ils sont destinés à être utilisés uniquement via leurs interfaces spécialisées .

Sur ma mise en œuvre prioroty_queue est décrit comme

/**
   *  @brief  A standard container automatically sorting its contents.
   *
   *  @ingroup sequences
   *
   *  This is not a true container, but an @e adaptor.  It holds
   *  another container, and provides a wrapper interface to that
   *  container.  The wrapper is what enforces priority-based sorting 
   *  and %queue behavior.  Very few of the standard container/sequence
   *  interface requirements are met (e.g., iterators).
   *
   *  The second template parameter defines the type of the underlying
   *  sequence/container.  It defaults to std::vector, but it can be
   *  any type that supports @c front(), @c Push_back, @c pop_back,
   *  and random-access iterators, such as std::deque or an
   *  appropriate user-defined type.
   *
   *  The third template parameter supplies the means of making
   *  priority comparisons.  It defaults to @c less<value_type> but
   *  can be anything defining a strict weak ordering.
   *
   *  Members not found in "normal" containers are @c container_type,
   *  which is a typedef for the second Sequence parameter, and @c
   *  Push, @c pop, and @c top, which are standard %queue operations.
   *  @note No equality/comparison operators are provided for
   *  %priority_queue.
   *  @note Sorting of the elements takes place as they are added to,
   *  and removed from, the %priority_queue using the
   *  %priority_queue's member functions.  If you access the elements
   *  by other means, and change their data such that the sorting
   *  order would be different, the %priority_queue will not re-sort
   *  the elements for you.  (How could it know to do so?)

modèle:

  template<typename _Tp, typename _Sequence = vector<_Tp>,
       typename _Compare  = less<typename _Sequence::value_type> >
    class priority_queue
    {

En face de cela, heap décrit comment ses éléments sont récupérés et stockés en mémoire. C'est une structure de données (basée sur un arbre) , les autres sont ie tableau, table de hachage, struct, union, set ..., qui satisfont en plus propriété du tas: tous les nœuds sont [supérieurs ou égaux à] ou [inférieurs ou égaux à] chacun de ses enfants, selon un prédicat de comparaison défini pour le segment de mémoire.

Donc, dans mon en-tête de tas, je ne trouve aucun conteneur de tas, mais plutôt un ensemble d'algorithmes

  /**
   * @defgroup heap_algorithms Heap Algorithms
   * @ingroup sorting_algorithms
   */ 

comme:

  • __is_heap_until
  • __is_heap
  • __Push_heap
  • __adjust_heap
  • __pop_heap
  • make_heap
  • sort_heap

tous (à l'exception de __is_heap, commenté comme "Cette fonction est une extension, ne fait pas partie de la norme C++") décrit comme

   *  @ingroup heap_algorithms
   *
   *  This operation... (what it  does)
2
4pie0

Pas vraiment. La "priorité" dans le nom découle d'une valeur de priorité pour les entrées de la file d'attente, définissant leur ... bien sûr: priorité. Il existe cependant de nombreuses façons de mettre en œuvre un tel PQ.

0
JensG