Structures de données IFT-2000 Abder Alikacem Les structures de piles et de files Édition Septembre 2009 Département d’informatique et de génie logiciel
Le point Bilan de la semaine 3 Les normes de programmation Le TP1
Comparaison des implantations de la liste Liste avec tableau: 1. Insertion et suppression sont O(n). 2. Précédent et accès direct sont O(1). 3. Tout l’espace est alloué à l’avance. 4. Pas d’espace autre que les valeurs. Liste dans une liste chaînée: 1. Insertion et suppression sont O(1). 2. Précédent et accès direct sont O(n). 3. L’espace augmente avec la liste. 4. Chaque élément requiert de l’espace pour les pointeurs
Laboratoire #4 (suite du lab#3… Implantation d’une liste ordonnée Nous vous fournissons 3 packetages, un par projet/implantation que vous devez faire: tableau dynamique liste doublement chaînée liste circulaire Dans chacun d'eux, vous devez compléter le fichier .inl étant donné le modèle d'implantation décrit dans la partie privée de la classe Liste. Bien entendu, les 3 implantations demandées doivent être génériques. Ces 2 main() fournis sont pour tester les 3 packetages (séparément), un main() pour instancier une liste ordonnée d'entiers et un autre pour déclarer une liste d'objets définis dans la classe ClasseTests.h que nous vous fournissons également.
Rappel des éléments du C++ à étudier Semaine 1 Du C au C++ Les entrées/sorties (important pour cette semaine) L'espace de nommage Les types vector et string Semaine 2 Concepts orientés objet Classe et objet Semaine 3 Programmation générique (template) La gestion des exceptions
Éléments du C++ à maîtriser les éléments syntaxiques nouveaux par rapport au langage C notion de classe, constructeurs, destructeur constructeur de copie et surcharge de l'op. = (minimum exigé dans une classe) les attributs et méthodes d'une classe: friend, static, const la généricité (template)
Le point sur les normes de programmation Commentaires d’interface Commentaires d’implémentation Découpage logique d’un programme La gestion des exceptions Style de programmation Voir sur le site Web du cours, section Documentations/Normes de programmation: NormesProgrammation.pdf Resume.h (à propos des commentaires Doxygen)
Résumé des balises de Doxygen L'interface ... /** * \brief * * \pre * \post * \exception */ Implémentation ... /** * \fn * * \param[in] * \param[out] * \return Section Documentations/Normes de programmation: Resume.h (à propos des commentaires Doxygen)
Spécifications « C++ » (version Doxygen) template <typename T> class Liste{ public://L'interface ... /** * \brief Ajouter un nouvel élément dans la liste * * \pre il y a assez de mémoire pour ajouter l'élément x * \pre la position d'ajout, pos, est comprise entre 1 et |L|+1 * \post la liste comprend un élément de plus * \post la liste est inchangée sinon * \exception range_error si la position est erronée * \exception length_error si pas assez de mémoire */ void ajouter(const T& x, int pos) throw(range_error, length_error); private: ...//Modèle d'implantation }; Fichier Liste.h
Commentaires d’implémentation (version Doxygen) /** * \fn void Liste<T>:: ajouter (const T& x, int pos) * * \param[in] x Élément à ajouter * \param[in] pos Position où insérer l'élément */ template <typename T> void Liste<T>::ajouter (const T& x, int pos) throw(out_of_range, length_error) {
TP1 Labyrinthe class ListePortes { class Porte Porte porte; { noeudListePortes *acces; } ; class Porte { Piece *destination; Couleur color; } TP1 class Piece { ListePortes portes; bool parcourue; string nom; }; NoeudListePieces premiere derniere … depart Classes Chemin et FilePieces : ils servent pour la résolution de certaines méthodes demandées arrivee Labyrinthe
Première partie: les piles Introduction et exemples d’application Description en terme de type abstrait Implantation
Piles et files Piles : LIFO : last in, first out DAPS : dernier arrivé, premier sorti in out pile Files : FIFO : first in, first out PAPS : premier arrivé, premier sorti out in file
Piles et files Structures de données auxiliaires car utilisées par d’autres structures (comme les listes ordonnées) Utilité : support à des applications support à d’autres structures de données modélisation de la réalité en informatique : système d’exploitation gestion interne évaluation d ’expressions etc.
Pile = liste + gestion adaptée manipulations (empiler et dépiler) par le même point d’accès out out in in pile pile
Piles espace de mémorisation temporaire, avec conventions de manipulation (gestion) : ajouter un nouvel élément sur la pile (empiler) enlever un élément de la pile (dépiler) regarder le premier élément de la pile indiquer si la pile est vide regarder si un élément est sur la pile remplacer un élément sur la pile les noms anglais pour ces opération sont: PUSH—EMPILER, POP—DÉPILER, TOP—SOMMET, PEEP—EXAMINER et CHANGE—CHANGER. pile
Piles Préoccupation de la programmation par objet initialiser une pile (constructeurs); détruire une pile de ses éléments (destructeur); Constructeur de copie; Surcharge de l’opérateur = pile
La classe Pile template <typename T> class Pile { public: // constructeurs et destructeurs Pile(); //constructeur Pile(const Pile&) throw(bad_alloc); //constructeur copie ~Pile(); //destructeur // Modificateurs void empiler(const T&) throw (bad_alloc); T depiler() throw(logic_error); //Sélecteurs bool estVide() const; int taille() const; T& sommet() const throw (logic_error); // élément au sommet //surcharge d'opérateurs Pile<T>& operator = (const Pile<T>&) throw (bad_alloc); friend ostream& operator << (ostream& , const Pile& ); private: … //Modèle d’implantation };
Piles : implantation dans un tableau Implantation dans un tableau dynamique template <typename T, int MAX = 100 > class Pile { public: Pile(const int max = MAX) throw (bad_alloc); // constructeur ... ~Pile (); // destructeur private : T* tab; int sommet; int tailleMax; };
Piles : implantation dans un tableau Constructeur template <Typename T> Pile<T> :: Pile (int max) throw (bad_alloc){ tab = new T [max]; sommet = -1; tailleMax =max; } Destructeur template <class T> Pile<T> :: ~Pile () { if (tab!=0) delete [ ] tab; }
Piles : implantation dans un tableau dépiler template <typename T> T Pile<T> :: depiler() throw (logic_error){ if (!estVide()) return tab[sommet--]; else throw logic_error("Depiler: la pile est vide!"); } Sélecteur : vide template <typename T> bool Pile<T> :: estVide () const { return (sommet == -1); }
Piles : implantation dans un tableau template <typename T> void Pile<T> :: empiler(const T& e) throw (length_error){ if (sommet+1 < tailleMax) { sommet ++; tab[sommet] = e; } else throw length_error("Empiler:la pile est pleine\n"); } empiler Remarque : on suppose que l ’opérateur d ’affectation existe ou est surchargé dans la classe qui correspond au paramètre T. => intérêt de la surcharge des opérateurs
Piles : implantation dans un tableau Surcharge de l’op. = template <typename T> Pile<T>& Pile<T> :: operator = (const Pile<T>& p) { if (tab!=0) delete [ ] tab; //on nettoie this tab=new T [p. tailleMax]; tailleMax =p.tailleMax; for (int i=0; i< tailleMax;i++) tab[i]=p.tab[i]; sommet=p.sommet; return (*this); //retourner : une référence sur l ’objet courant }
Piles : implantation dans un tableau Constructeur de copie template <typename T> Pile<T> :: Pile (const Pile<T>& p) { tab=new X [p. tailleMax]; tailleMax =p. tailleMax; for (int i=0; i< tailleMax;i+=1) tab[i]=p.tab[i]; sommet=p.sommet; }
Piles : implantation dans une liste chaînée Version 1 template <typename T> class Noeud{ public: Noeud (const T& data_item, Noeud * next_ptr = 0) : el(data_item), suivant(next_ptr) {}//constructeur ~Noeud () {}; //destructeur qui ne fait rien }; friend class Pile <T>; private : T el; Noeud<T> * suivant;
Piles : implantation dans une liste chaînée template <typename T> class Pile { public: Pile (); // Constructeur Pile (const pile<X>&) throw (bad_alloc); // Constructeur par copie ~Pile (); // Destructeur Pile<T>& operator=(const Pile<T>& P); // P1 = P void empiler (const T& a) throw (bad_alloc); T depiler () throw (logic_error); T& sommet() const throw (logic_error); // sélecteur : valeur //placée au sommet bool vide() const; //sélecteur : est vide? }; Version 1 private : Noeud<T>* sommet; // pointeur sur le sommet de la pile int cpt; // Nombre d'élements de la pile void detruire (); //Méthode privée utile pour le destructeur // et l’op. =
Piles : implantation dans une liste chaînée template <typename T> class Pile { public: Pile (); Pile (const Pile<T>&) throw (bad_alloc); ~Pile (); Pile<T>& operator=(const Pile<T>& P); //.. private: class Noeud{ public: T el; Noeud * suivant; Noeud (const T& data_item, Noeud * next_ptr = 0) : el(data_item), suivant(next_ptr) {} }; typedef Noeud * elem; elem sommet; //sommet de la pile int cpt; void detruire (); Version 2 Class Nœud interne
Piles : implantation dans une liste chaînée destructeur constructeur template <class X> Pile<X>:: ~Pile() { if (sommet != 0) detruire( );} template <typename T> Pile<T>::Pile () { sommet =0; cpt = 0; } template <typename T> void Pile<T>::detruire () { Noeud<T>* p; while (sommet != 0){ p=sommet->suivant; delete sommet; sommet=p; }
Les piles Comparaison des 2 implantations Toutes les implantations des opérations d’une pile utilisant un tableau ou une liste chaînée prennent un temps constant i.e. en O(1). Par conséquent, d’un point de vue d’efficacité, aucune des deux implantations n’est mieux que l’autre. D’un point de vue de complexité spatiale, le tableau doit déclarer une taille fixe initialement. Une partie de cet espace est perdue quand la pile n’est pas bien remplie. La liste peut augmenter ou rapetisser au besoin mais demande un extra espace pour mémoriser les adresses des pointeurs.
Applications des piles Vérification de parenthèses Reconnaissance syntaxique Calcul arithmétique
Deuxième partie: les files Introduction et exemples d’application Description en terme de type abstrait Implantation
Files Files : FIFO : first in, first out PAPS : premier arrivé, premier sorti Manipulations (enfiler et défiler) par des points d’accès opposés in out out in file file
Files Description en termes de type abstrait Le rôle d'une file est de permettre la mise en attente d'informations dans le but de les récupérer plus tard. La première information à être récupérée est celle qui a été mise en attente en premier. Primitives de manipulation: 1. ajouter un nouvel élément dans la file (enfiler); 2. ôter l'élément le plus ancien de la file (défiler); 3. indiquer si la file d'attente est vide; 4. retourner le premier et dernier élément; Etc..
Files Préoccupation de la programmation par objet initialiser une file (constructeurs); détruire une file de ses éléments (destructeur); Constructeur de copie; Surcharge de l’opérateur = in file
La classe File template<typename T> class File { public: // constructeurs et destructeurs: File() throw(bad_alloc); //constructeur File(const File &) throw(bad_alloc); //constructeur copie ~File(); // modificateurs void enfiler(const T&) throw (length_error); T defiler() throw(logic_error); // sélecteurs int taille() const { return cpt;} bool estVide() const { return cpt==0; } bool estPleine() const { return cpt==TailleMax; } T& premier() const throw (logic_error); // élément en tête de la file T& dernier() const throw (logic_error); // élément en queue de la file // surcharges d'opérateurs File<T>& operator = (const File<T>&) throw (bad_alloc); friend ostream& operator << (ostream& f, const File& q); private: // ...Modèle d'implantation };
Implantation par tableau circulaire V i d e T ê t e Q u e u e P a u l I n s é r e r ' P a u l ' T Q P a u l R e n é I n s é r e r ' R e n é ' T Q P a u l R e n é M a r c I n s é r e r ' M a r c ' T Q R e n é M a r c E x t r a i r e ' P a u l ' T Q M a r c E x t r a i r e ' R e n é ' T Q M a r c J e a n I n s é r e r ' J e a n ' T Q M a r c J e a n A n n e I n s é r e r ' A n n e ' T Q M a r c J e a n A n n e I n s é r e r ' R o b e r t ' T Q D é b o r d e m e n t
Implantation: liste circulaire q = (q + 1) modulo MAXELT
Files: implantation dans un tableau Implantation dans un tableau dynamique template <typename T, in MAX = 100 > class File { public: File(const int max = MAX) throw (bad_alloc); // constructeur ... ~File (); // destructeur private : T* tab; int tete; int queue; int tailleMax; int cpt; }; Tableau tab réservé dans le tas Constructeur avec un paramètre : taille maximum
Files: implantation dans un tableau Constructeur template <Typename T> File<T> :: File (int max) throw (bad_alloc){ tab = new T [max]; tete = 0; queue = 0; cpt = 0; tailleMax =max; } Destructeur template <class X> File<X> :: ~File () { if (tab!=0) delete [ ] tab; }
Files: implantation dans un tableau défiler template <typename T> T File<T> :: defiler() throw (logic_error){ if (cpt!=0) { T elementaDefiler = tab[tete]; tete = (tete+1)% tailleMax; cpt--; return elementaDefiler; } else throw logic_error("Defiler: la file est vide!");
Files: implantation dans un tableau enfiler template <typename T> T File<T> :: enfiler(const T& e) throw (length_error){ if(cpt<tailleMax) { tab[queue]= e; queue = (queue+1)%tailleMax; cpt++; } else throw length_error(« Enfiler: la file est pleine!");
Files: implantation par listes template <typename T> class File { public: File (); File (const File<T>&) throw (bad_alloc); ~File (); File<T>& operator=(const File<T>& P); //.. private: class Noeud{ public: T el; Noeud * suivant; Noeud (const T& data_item, Noeud * next_ptr = 0) : el(data_item), suivant(next_ptr) {} }; typedef Noeud * elem; elem tete; // pointeur sur la tête de la file eleme queue; // Pointeur en queue de file int cpt; // cardinalité de la file Implantation dans une liste chaînée
Files: implantation par listes destructeur constructeur template <class X> File<X>:: ~File() { if (tete != 0) Noeud<T>* p; while (tete != 0){ p=tete->suivant; delete tete; tete=p; } template <typename T> Pile<T>::Pile () { tete =0; queue = 0; cpt = 0; }
Files prioritaires 1 4 4 8 1 1 1 4 4 8 file file Gestion d’une seule file insertion se fait selon la priorité éléments toujours triés selon leur priorité 1 4 4 8 1 1 1 4 4 8 in in file file
Files prioritaires Gestion de plusieurs files 1 file par niveau de priorité (sous-liste) une liste triée selon le niveau de priorité avec un bloc descripteur pour la sous-liste correspondante
Applications des files Modélisation du phénomène: banque chaîne de montage etc. En informatique: traitement « batch » (en lots) gestion des listes d’impression
Principes d’une simulation Processus consommateur simulation: élément = <événement,temps> priorité = temps Tantque toujours faire si f n’est pas vide alors: tâche = defile(f); exécuter tâche; Fin Tantque 1 4 4 8 1 1 1 4 4 8 in in Processus producteur Si exécution(tâche) alors: f = enfiler(tâche,f); file file
Laboratoire #5 Le but de ce laboratoire est l’implantation de l’interface d’une pile et d’une file génériques. Parmi les exemples de cette semaine, nous vous avons fourni des implantations d’une pile dans un tableau dynamique et d’une file dans une liste simplement chaînée. Il est question ici de refaire l’implantation d’une pile et d’une file dans une liste simplement chaînée et dans un tableau dynamique respectivement. Il est question également de l’implantation d’une pile dans un modèle hybride et d’une file prioritaire. Téléchargez l'ensemble de l'énoncé. Vous y trouverez entre autres, 2 packetages, une pour une pile et l'autre pour une file. Pour chacun d'eux, vous devez compléter le fichier .inl étant donné le modèle d'implantation décrit dans la partie privée de la classe décrite dans chaque packetage. Nous vous fournissons un main() pour tester chaque implantation.