Les piles en C++

20 Nov 2021 20 Nov 2021 9196 vues ESSADDOUKI Mostafa 7 min de lecture
Introduction et syntaxe de base
1 Introduction au langage C++ 2 Entrée-sortie en C++ - cin et cout 3 Inférence de type avec le mot-clé auto en C++ 4 Classe std::string et les chaînes de caractères en C++ 5 Les structures conditionnelles (if et switch) en C++ (C++17 et C++20) 6 Les boucles en C++ (C++17 et C++20) 7 La gestion des fichiers en C++
Pointeurs et fonctions
8 Introduction aux pointeurs en C++ - Déclaration et interêts 9 Les références en C++ - déclaration et interêts 10 Les tableaux en C++ - Déclaration et interêts 11 Introduction aux fonctions en C++ 12 Passer des arguments à une fonction en C++ 13 Déclarer un paramètre const en C++ 14 Les fonctions Lambda en C++ 15 Fonctions utiles (Mathématiques et caractères) en C++
Programmation OO
16 Classes et objets en C++ 17 Spécificateurs d'accès en C++ 18 Constructeurs et destructeur d'une classe en C++ 19 Fonctions membres en C++ 20 Membres statiques d'une classe en C++ 21 Fonctions en ligne en C++ - inline 22 Fonctions et classes amies en C++ - friend 23 Surcharge des fonctions en C++ 24 Surcharge des opérateurs en C++ 25 Héritage en C++ 26 La gestion d'exceptions en C++ : déclaration, utilisation et personnalisation 27 fonctions et classes templates en C++ 28 Les nouveautés C++20 pour améliorer les templates en C++
Structures de données
29 Introduction aux structures de données 30 Les structures en C++ et la différence avec les structures en C 31 Les listes chaînées en C++ 32 Les piles en C++ 33 File d'attente en C++ 34 Arbre binaire de recherche : définition et mise en oeuvre en C++
La bibliothèque standard (STL)
35 Introduction à la bibliothèque de Template Standard STL 36 Les itérateurs en C++ - définition, déclaration et exemples 37 La classe array en C++ (bibliothèque STL) <array> 38 La classe vector de la bibliothèque STL <vector> 39 La classe deque en C++ ( Bibliothèque STL) 40 La classe list en C++ (bibliothèque STL) <list> 41 La classe stack (Pile) en C++ (bibliothèque STL) <stack> 42 La classe queue (File d'attente) en C++ (bibliothèque STL) <queue> 43 La file d'attente prioritaire (classe priority_queue) - Bibliothèque STL 44 Les ensembles en C++ (Classe set <set> - Bibliothèque STL) 45 Les dictionnaires en C++ : Classe map (Bibliothèque STL) 46 Introduction aux algorithmes de la bibliothèque STL (programmation compétitive) 47 Tri et méthodes associées en C++ - Bibliothèque STL 48 Recherche dichotomique et méthodes associées en C++ - Bibliothèque STL 49 Appliquer un prédicat ou une fonction aux éléments d'une séquence en C++ - Bibliothèque STL 50 Recherche dans une séquence et méthodes associées en C++ - Bibliothèque STL

La structure Pile (Stack)

Une pile est un conteneur linéaire dans lequel tous les ajouts et suppressions sont limités à une seule extrémité appelée tête de pile. Si on insère les éléments {5, 10, 8, 20}, ils seront supprimés dans l'ordre inverse {20, 8, 10, 5}.

Principe LIFO Une pile est une structure LIFO (Last In, First Out — dernier entré, premier sorti). Le dernier élément inséré est le premier à être retiré, comme une pile de livres ou de disques dans la vie courante.

Représentation d'une pile

Pile LIFO

Opérations de base

Opérations empiler, dépiler, premier

OpérationDescription
empiler(valeur)Insère un élément en tête de pile
depiler()Supprime l'élément en tête de pile
premier()Accède à l'élément en tête sans le supprimer
Implémentation : tableau vs liste chaînée L'implémentation par tableau est plus simple, mais impose une taille fixe à la compilation. L'implémentation par liste chaînée permet à la pile de grandir et rétrécir dynamiquement : les nœuds sont alloués dans le tas, l'objet pile dans la pile mémoire.

Pile implémentée en liste chaînée

Implémentation directe (sans réutilisation de Liste)

Déclaration — pile.h

#ifndef PILE_H
#define PILE_H

#include <iostream>
#include <cassert>
using namespace std;

template <typename T>
struct Noeud {
    T donnees;
    Noeud<T>* suivant;
};

template <typename T>
class Pile {
private:
    Noeud<T>* entete;
    int compteur;
    Noeud<T>* CreerNoeud(const T& valeur);

public:
    Pile();
    ~Pile();
    void empiler(const T& valeur);
    void depiler();
    T& premier() const;
    void afficher() const;
    int taille() const;
};
#endif

Empiler un élément

L'insertion se fait toujours en tête — deux opérations suffisent après la création du nœud.

Empiler — insertion en tête

template <typename T>
void Pile<T>::empiler(const T& valeur) {
    Noeud<T>* nouveau = CreerNoeud(valeur);
    nouveau->suivant = entete;
    entete = nouveau;
    compteur++;
}

Dépiler l'élément en tête

La suppression du premier nœud libère sa mémoire et avance l'entête.

Dépiler — suppression en tête

template <typename T>
void Pile<T>::depiler() {
    if (compteur == 0) {
        cout << "Erreur! La pile est vide." << endl;
        return;
    }
    Noeud<T>* del = entete;
    entete = entete->suivant;
    delete del;
    compteur--;
}

Retourner le premier élément

template <typename T>
T& Pile<T>::premier() const {
    if (compteur == 0) {
        cout << "Erreur! La pile est vide";
        assert(false);
    }
    return entete->donnees;
}

Fichiers complets — implémentation directe

#ifndef PILE_H
#define PILE_H

#include <iostream>
#include <cassert>
using namespace std;

template <typename T>
struct Noeud {
    T donnees;
    Noeud<T>* suivant;
};

template <typename T>
class Pile {
private:
    Noeud<T>* entete;
    int compteur;
    Noeud<T>* CreerNoeud(const T& valeur);

public:
    Pile();
    ~Pile();
    void empiler(const T& valeur);
    void depiler();
    T& premier() const;
    void afficher() const;
    int taille() const;
};
#endif
#ifndef PILE_CPP
#define PILE_CPP

#include "pile.h"

template <typename T>
Pile<T>::Pile() : entete(NULL), compteur(0) {}

template <typename T>
Pile<T>::~Pile() {
    Noeud<T>* del = entete;
    while (entete) {
        entete = entete->suivant;
        delete del;
        del = entete;
    }
}

template <typename T>
Noeud<T>* Pile<T>::CreerNoeud(const T& valeur) {
    Noeud<T>* temp = new Noeud<T>;
    temp->donnees = valeur;
    temp->suivant = NULL;
    return temp;
}

template <typename T>
void Pile<T>::empiler(const T& valeur) {
    Noeud<T>* nouveau = CreerNoeud(valeur);
    nouveau->suivant = entete;
    entete = nouveau;
    compteur++;
}

template <typename T>
void Pile<T>::depiler() {
    if (compteur == 0) { cout << "Erreur! La pile est vide." << endl; return; }
    Noeud<T>* del = entete;
    entete = entete->suivant;
    delete del;
    compteur--;
}

template <typename T>
T& Pile<T>::premier() const {
    if (compteur == 0) { cout << "Erreur! La pile est vide"; assert(false); }
    return entete->donnees;
}

template <typename T>
void Pile<T>::afficher() const {
    if (compteur == 0) { cout << "La pile est vide!" << endl; return; }
    Noeud<T>* courant = entete;
    while (courant != 0) {
        cout << courant->donnees << endl;
        courant = courant->suivant;
    }
}

template <typename T>
int Pile<T>::taille() const { return compteur; }

#endif

Implémentation par composition (réutilisation de la classe Liste)

Plutôt que de tout réécrire, on peut réutiliser la classe Liste par composition : la pile contient un objet liste et restreint son interface aux trois opérations utiles.

Composition vs Héritage Un objet Pile n'est pas un objet Liste — il en a moins de fonctionnalités. On préfère donc la composition (la pile possède une liste) plutôt que l'héritage.
#ifndef PILE_H
#define PILE_H
#include "liste.cpp"

template <typename T>
class Pile {
private:
    Liste<T> liste;   // composition : la pile contient une liste

public:
    void empiler(const T& valeur);
    void depiler();
    T& premier() const;
    void afficher() const;
    int taille() const;
};
#endif
#ifndef PILE_CPP
#define PILE_CPP

#include "pile.h"

template <typename T>
void Pile<T>::empiler(const T& valeur) {
    liste.inserer(0, valeur);   // toujours en position 0 (tête)
}

template <typename T>
void Pile<T>::depiler() {
    liste.supprimer(0);
}

template <typename T>
T& Pile<T>::premier() const {
    return liste.getNoeud(0);
}

template <typename T>
void Pile<T>::afficher() const {
    liste.afficher();
}

template <typename T>
int Pile<T>::taille() const {
    return liste.taille();
}
#endif

Programme principal

#include "pile.cpp"
#include <string>

int main() {
    Pile<string> pile;

    pile.empiler("Mostafa");
    pile.empiler("Omar");
    pile.empiler("Sara");
    pile.empiler("Mohamed");
    pile.empiler("Moneim");
    pile.empiler("Dounia");
    pile.empiler("Abdelmalek");

    cout << "Afficher la pile" << endl;
    pile.afficher();
    cout << '\n';

    cout << "Afficher la tete de la pile" << endl;
    cout << pile.premier() << endl;
    cout << '\n';

    cout << "Supprimer quelques noeuds et afficher la pile" << endl;
    pile.depiler();
    pile.depiler();
    pile.afficher();
    cout << '\n';

    cout << "Afficher la tete de la pile" << endl;
    cout << pile.premier() << endl;
    cout << '\n';

    cout << "Nombre de noeuds dans la pile : " << pile.taille();
    return 0;
}
Sortie
Afficher la pile
Abdelmalek
Dounia
Moneim
Mohamed
Sara
Omar
Mostafa

Afficher la tete de la pile
Abdelmalek

Supprimer quelques noeuds et afficher la pile
Moneim
Mohamed
Sara
Omar
Mostafa

Afficher la tete de la pile
Moneim

Nombre de noeuds dans la pile : 5

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.