Choisissez votre style : colorisé, impression

Correction 25
Structures de données et templates


Exercice 1 : Piles et parenthèses (SDA, niveau 1)

Une variante non-objet de cette exercice existe sous : Exercice n°39 (page 93 et 274) de l'ouvrage C++ par la pratique.

En suivant la démarche indiquée dans l'exercice, voici un résultat possible  :

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
using namespace std;

// un type pour le contenu de la pile pour faciliter d'éventuelles 
// modifications.
typedef char Type_el;


// Implémentation simple de la SDA Pile
class Pile {
public:
  void empile(Type_el e);
  void depile();
  Type_el top();
  bool est_vide();
// autres fonctions ne faisant pas partie des spécifications
// usuelles des piles
  friend ostream& operator<<(ostream&, Pile&);
  void vide();
private:
  vector<Type_el< data;
};

// fonction pour le controle du parenthesage d'une expression
bool check(string parentheses);


//----------------------------------------------------------------------
int main()
{
  string s;

  do {
    cout << "Entrez une expresssion parenthèsée : " << flush;
    getline(cin, s);
    if (!s.empty()) 
      cout << " -> " << (check(s) ? "OK" : "Erreur") << endl;
  } while (!s.empty());
  return 0;
}

void Pile::empile(Type_el e)
{
 data.push_back(e);
}

void Pile::depile ()
{
  if (!est_vide()) data.pop_back();
}

Type_el Pile::top ()
{
  if (!est_vide())
    return data[data.size()-1];
  else // que faire ??  -> normalement lever une exception
    // mais version simplifiée ici
    return 0;
  // 
}

bool Pile::est_vide()
{
  return data.empty();
}

void Pile::vide()

{
  data.clear();
}

ostream& operator<<(ostream& o, Pile& pile)
{
  for (unsigned int i(pile.data.size()-1); i > 0; i--)
    cout << "|" << setw(5) << pile.data[i] <<"|" <<endl;
  if (!pile.data.empty()) cout << "|" << setw(5) << pile.data[0] << "|" << endl;
  cout << "|_____|" << endl;
  return o;
}


bool check(string s) {
  Pile p;
  for (unsigned int i(0); i < s.size(); i++) {
    if ((s[i] == '(') || (s[i] == '['))
      p.empile(s[i]);
    else if (s[i] == ')') {
      if ((!p.est_vide()) && (p.top() == '('))
	p.depile(); 
      else 
	return false;
    } else if (s[i] == ']') {
      if ((!p.est_vide()) && (p.top() == '['))
	p.depile(); 
      else 
	return false;
    }
  }

  return p.est_vide();
}

Avec cette implémentation de la SDA pile, une pile ne peut contenir que des char. Si l'on veut changer le type du contenu de la pile, il est nécessaire de modifier la définition du type Type_el et recompiler.


Exercice 2 : Piles et parenthèses revisitées (SDA, template, niveau 2)

La principale difficulté ici est l'externalisation des définitions des méthodes de la pile, lorsque cette SDA devient un template.

Il est aussi intéressant d'étudier le codage de la surcharge de l'opérateur << (non demandé dans l'exercice).

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
using namespace std;

template
class Pile {
public:
  void empile(Type_el e);
  void depile();
  Type_el top();
  bool est_vide();
// autres fonctions ne faisant pas partie des spécifications
// usuelles des piles
  template<typename T>friend ostream& operator<<(ostream&, Pile&);
  void vide();
private:
  vector<Type_el> data;
};

//
bool check(string parentheses);


//----------------------------------------------------------------------
int main()
{
  string s;

  do {
    cout <<"Entrez une expresssion parenthèsée : " << flush;
    getline(cin, s);
    if (!s.empty()) 
      cout << " -> " << (check(s) ? "OK" : "Erreur") << endl;
  } while (!s.empty());
  return 0;
}

template<typename Type_el>void Pile<Type_el>::empile(Type_el e)
{
 data.push_back(e);
}

template<typename Type_el>void Pile<Type_el>::depile ()
{
  if (!est_vide()) data.pop_back();
}

template<typename Type_el>Type_el Pile<Type_el>::top ()
{
  if (!est_vide())
    return data[data.size()-1];
  else // que faire ??  -> normalement lever une exception
    // mais version simplifiée ici
    return 0;
  // 
}

template<typename Type_el>bool Pile<Type_el>::est_vide()
{
  return data.empty();
}

template<typename Type_el>void Pile<Type_el>::vide()
{
  data.clear();
}

template<typename T> ostream& operator<<(ostream& o, Pile<T>& pile)
{
  for (unsigned int i(pile.data.size()-1); i > 0; i--)
    cout << "|" << setw(5) << pile.data[i] << "|" << endl;
  if (!pile.data.empty()) cout << "|" << setw(5) << pile.data[0] << "|" << endl;
  cout << "|_____|" << endl;
  return o;
}


bool check(string s) {
  Pile<char> p;
  for (unsigned int i(0); i < s.size(); i++) {
    if ((s[i] == '(') || (s[i] == '['))
      p.empile(s[i]);
    else if (s[i] == ')') {
      if ((!p.est_vide()) && (p.top() == '('))
	p.depile(); 
      else 
	return false;
    } else if (s[i] == ']') {
      if ((!p.est_vide()) && (p.top() == '['))
	p.depile(); 
      else 
	return false;
    }
  }

  return p.est_vide();
Et voici une variante (niveau 3) où Pile hérite de vector. La principale difficulté réside dans les modalités d'utilisation des méthodes héritées de vector. Les éléments spécifiques à cette variante sont en gras dans la solution proposée ci-dessous :
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
using namespace std;

template<typename Type_el>
class Pile : private vector<Type_el>{
public:
  void empile(Type_el e);
  void depile();
  Type_el top();
  bool est_vide();
// autres méthode ne faisant pas partie des spécifications
// usuelles des piles
  template<typename T>friend ostream& operator<<(ostream&, Pile<T>&);
  void vide();
};

//
bool check(string parentheses);


//----------------------------------------------------------------------
int main()
{
  string s;

  do {
    cout << "Entrez une expresssion parenthèsée : " << flush;
    getline(cin, s);
    if (!s.empty()) 
      cout << " -> " << (check(s) ? "OK" : "Erreur") << endl;
  } while (!s.empty());
  return 0;
}

template<typename Type_el> void Pile<Type_el>::empile(Type_el e)
{
 push_back(e);
}

template<typename Type_el> void Pile<Type_el>::depile ()
{
  if (!est_vide()) vector<Type_el>::pop_back();
}

template<typename Type_el>  Type_el Pile<Type_el>::top ()
{
  if (!est_vide())
    return (*this)[vector<Type_el>::size()-1];
  else // que faire ??  -> normalement lever une exception
    // mais version simplifiée ici
    return 0;
  // 
}

template<typename Type_el> bool Pile<Type_el>::est_vide()
{
  return vector<Type_el>::empty();
}

template<typename Type_el> void Pile<Type_el>::vide()
{
  vector<Type_el>::clear();
}

template<typename T>ostream& operator<<(ostream& o, Pile<T>& p)
{
  for (unsigned int i(p.size()-1); i > 0; i--)
    cout << "|" << setw(5) << p[i] << "|" << endl;
  if (!p.empty()) cout << "|" << setw(5) << p[0] << "|" << endl;
  cout << "|_____|" << endl;
  return o;
}


bool check(string s) {
  Pile<char> p;
  for (unsigned int i(0); i < s.size(); i++) {
    if ((s[i] == '(') || (s[i] == '[')){
      p.empile(s[i]);
      cout << p << endl;
    }
    else if (s[i] == ')') {
      if ((!p.est_vide()) && (p.top() == '('))
	p.depile(); 
      else 
	return false;
    } else if (s[i] == ']') {
      if ((!p.est_vide()) && (p.top() == '['))
	p.depile(); 
      else 
	return false;
    }
  }

  return p.est_vide();
}

Exercice 3 : Piles et notation polonaise inverse (SDA, template, niveau 2)

Une variante non-objet de cet exercice se trouve sous : Exercice n°41 (page 96 et 281) de l'ouvrage C++ par la pratique.

Voici une solution possible pour cet exercice :

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
using namespace std;

template<typename Type_el>
class Pile {
public:
  void empile(Type_el e);
  void depile();
  Type_el top();
  bool est_vide();
// autres fonctions ne faisant pas partie des spécifications
// usuelles des piles
  template<typename T>friend ostream& operator<<(ostream&, Pile<T>&);
  void vide();
private:
  vector<Type_el> data;
};

//
int eval(string entree);


//----------------------------------------------------------------------
int main()
{
  string s;

  do {
    cout << "Entrez une expresssion à évaluer : " << flush;
    getline(cin, s);
    if (!s.empty()) 
      cout << " -> résultat : " << eval(s) << endl;
  } while (!s.empty());
  return 0;
}


int eval(string s) 
{
  Pile<int> p;
  
  // recopie dans la pile
  for (unsigned int i(0); i < s.size(); i++)
    if ((s[i] >= '0') && (s[i] <= '9')) {
      // On a lu un chiffre
      p.empile(int(s[i] - '0'));
    } else if ((s[i] == '+') || (s[i] == '-') || 
	       (s[i] == '*') || (s[i] == '/')) {

      // recupere le second argument
      int y(p.top());
      p.depile();

      // recupere le premier argument
      int x(p.top());
      p.depile();

      // calcule et empile le resultat
      switch(s[i]) {
      case '+': p.empile(x + y); break;
      case '-': p.empile(x - y); break;
      case '*': p.empile(x * y); break;
      case '/': p.empile(x / y); break;
      }
    }

  return p.top();
}

template<typename Type_el>void Pile<Type_el>::empile(Type_el e)
{
 data.push_back(e);
}

template<typename Type_el>void Pile<Type_el>::depile ()
{
  if (!est_vide()) data.pop_back();
}

template<typename Type_el>Type_el Pile<Type_el>::top ()
{
  if (!est_vide())
    return data[data.size()-1];
  else // que faire ??  -> normalement lever une exception
    // mais version simplifiée ici
    return 0;
  // 
}

template<typename Type_el>bool Pile<Type_el>::est_vide()
{
  return data.empty();
}

template<typename Type_el>void Pile<Type_el>::vide()
{
  data.clear();
}

template<typename T> ostream& operator<<(ostream& o, Pile<T>& pile)
{
  for (unsigned int i(pile.data.size()-1); i > 0; i--)
    cout << "|" << setw(5) << pile.data[i] << "|" << endl;
  if (!pile.data.empty()) cout << "|" << setw(5) << pile.data[0] << "|" << endl;
  cout << "|_____|" << endl;
  return o;
}

Dernière mise à jour : 2022/04/29 11:04:03