Aller au contenu

Structures de données linéaires - les piles

I. Introduction⚓︎

Il faut se représenter une pile comme... une pile de livres ! Seul le livre disposé sur le dessus est accessible : l'ajout et le retrait d'un livre ne peut donc se faire que sur le sommet de la pile.

LIFO

On dit que les piles sont en mode LIFO (Last In, First Out qui signifie « dernier entré, premier sorti »).

pile LIFO

On ajoute des livres sur la pile, et on les récupère en commençant par le dernier ajouté

Usage courant d'une pile

😊 Les piles sont très utilisées en informatique, en voici quelques usages caractéristiques :

  • Les algorithmes récursifs utilisent une pile d'appel pour mémoriser les contextes d'exécution de chaque appel.
  • La fonction «Annuler la frappe» (en anglais «Undo») d'un traitement de texte mémorise les modifications apportées au texte dans une pile.
  • Comme nous le verrons plus tard dans l'année, on peut aussi utiliser une pile pour parcourir (en profondeur) un graphe et mémoriser les sommets visités.
  • La vérification du bon parenthésage d'une expression peut également se faire à l'aide d'une pile.

Les fonctions primitives pour les PILES sont les suivantes

  • création d'une pile vide (oublié sur l'illustration),
  • tester si une pile est vide,
  • empiler,
  • dépiler.

😊 Et rien de plus ... Pile Gilles

Image crée par Gilles Lassus

Un jeu sérieux

Pour comprendre le principe, vous pouvez jouer à ce jeu :

OCTAVES FLUSH

Représentation possible d'une pile et exemple

🌵🌵 Il n'existe pas une façon "universelle" de représenter les piles. dans cet exemple le sommet sera indiqué avec le symbole > et le fond avec le symbole ]

Une pile contenant les éléments 'a', 'b' et 'c' ('a' étant le sommet et donc 'c' le fond de la pile) sera représentée ici de la façon suivante :

>'a', 'b', 'c']

Exemple : On considère la pile P : >'a', 'b', 'c']. Voici comment la manipuler :

Opération Contenu de la pile
empiler(P, 'e') >'e', 'a', 'b', 'c']
depiler(P) >'a', 'b', 'c']
depiler(P) >'b', 'c']
depiler(P) >'c']
empiler(P, 'm') >'m', 'c']

II. Une implémentation possible des piles⚓︎

Mon info

On donne ci-dessous une possible implémentation de ces fonctions primaires, en utilisant le vocabulaire de la programmation orientée objet que nous avons déjà abordée.

Il existe bien d'autres possibilités pour implémenter ces fonctions primaires.

Dans toute la suite les piles seront affichées entre crochets, comme des list python. Le sommet de la pile est l'élément écrit le plus à droite. Ainsi, si l'on part d'une pile vide, et que l'on empile successivement les entiers 1, puis 2, puis 3, on obtiendra une pile qui s'affichera de la façon suivante : [1, 2, 3]. Le sommet de cette pile est l'entier 3.

La classe Pile

Exécuter absolument le code ci-dessous pour pouvoir continuer.

###
class Pile :bksl-nl def py-undpy-undinitpy-undpy-und(self):bksl-nl self.contenu = []bksl-nlbksl-nl def estpy-undvidepy-undpile(self) :bksl-nl return self.contenu == []bksl-nlbksl-nl def empiler(self, x):bksl-nl self.contenu.append(x)bksl-nlbksl-nl def depiler(self):bksl-nl assert not self.estpy-undvidepy-undpile(), "la pile est vide"bksl-nl return self.contenu.pop()bksl-nlbksl-nl def py-undpy-undstrpy-undpy-und(self) : # ceci n'est pas une primitive mais utile pour voir la pilebksl-nl return str(self.contenu) + " <- sommet"bksl-nlbksl-nlbksl-nlbksl-nl



Tester ci-dessous ces primitives

###
pilepy-und1 = Pile()bksl-nlpilepy-und1.empiler("a")bksl-nlprint(pilepy-und1)bksl-nlpilepy-und1.empiler("b")bksl-nlprint(pilepy-und1)bksl-nlbksl-nl



Imaginez vos propres tests

###

A vous de jouer

Ecrire ci dessous les instructions qui permettent d'obtenir successivement les affichages suivants :

[12] <- sommet
[12, 14] <- sommet
[12, 14, 8] <- sommet
[12, 14] <- sommet
[12] <- sommet
[] <- sommet

###

Solution
🐍 Script Python
pile_1 = Pile()
pile_1.empiler(12)
print(pile_1)
pile_1.empiler(14)
print(pile_1)
pile_1.empiler(8)
print(pile_1)
pile_1.depiler()
print(pile_1)
pile_1.depiler()
print(pile_1)
pile_1.depiler()
print(pile_1)
La taille d'une pile

Alice désire écrire une fonction, qui doit retourner la taille de la pile.
Attention, une fois que sa taille a été déterminée, la pile ne doit pas avoir été modifiée...
Elle propose le code suivant. L'exécuter absolument

###
def taillepy-undessai(pile):bksl-nl resultat = 0bksl-nl while not pile.estpy-undvidepy-undpile():bksl-nl pile.depiler()bksl-nl resultat = resultat + 1bksl-nl return resultatbksl-nlbksl-nlbksl-nl



Test du code d'Alice

Tester

###
pilepy-und1 = Pile()bksl-nlpilepy-und1.empiler("a")bksl-nlpilepy-und1.empiler("b")bksl-nlprint("Au début", pilepy-und1)bksl-nlprint("Taille de la pile : ", taillepy-undessai(pilepy-und1))bksl-nlprint("A la fin", pilepy-und1)bksl-nlbksl-nlbksl-nlbksl-nl



Quel est le problème ?

Pourquoi cette solution ne convient-elle pas ?

Solution

On a bien déterminé la taille de la pile, mais on l'a détruite 😰

Une nouvelle fonction

Ecrire ci-dessous une fonction taille qui remédie à ce problème

Astuce à lire en désespoir de cause ...

Vous pouvez utiliser une deuxième pile ...

###
# Testsbksl-nlpilepy-und1 = Pile()bksl-nlpilepy-und1.empiler("a")bksl-nlpilepy-und1.empiler("b")bksl-nlassert taille(pilepy-und1) == 2bksl-nlbksl-nl# Autres testsbksl-nlpilepy-und1.empiler("c")bksl-nlassert taille(pilepy-und1) == 3bksl-nlpilepy-und3 = Pile()bksl-nlassert taille(pilepy-und3) == 0bksl-nlbksl-nlbksl-nlbksl-nlbksl-nl 5/5
def taille(pile):bksl-nl ...bksl-nlbksl-nlpilepy-und1 = Pile()bksl-nlpilepy-und1.empiler("a")bksl-nlpilepy-und1.empiler("b")bksl-nlprint("Au début", pilepy-und1)bksl-nlprint("Taille de la pile : ", taille(pilepy-und1))bksl-nlprint("A la fin", pilepy-und1)bksl-nlbksl-nl# Testsbksl-nlassert taille(pilepy-und1) == 2bksl-nlbksl-nlbksl-nlbksl-nldef taille(pile):bksl-nl pilepy-und2 = Pile()bksl-nl resultat = 0bksl-nl while not pile.estpy-undvidepy-undpile():bksl-nl pilepy-und2.empiler(pile.depiler())bksl-nl resultat = resultat + 1bksl-nl while not pilepy-und2.estpy-undvidepy-undpile():bksl-nl pile.empiler(pilepy-und2.depiler())bksl-nl return resultatbksl-nlbksl-nlbksl-nl



III. Vérifier les parenthèses d'une expression mathématique⚓︎

Le but de cet exercice est d'écrire une fonction qui contrôle si une expression mathématique, donnée sous forme d'une chaîne de caractères, est bien parenthésée, c'est-à-dire s'il y a autant de parenthèses ouvrantes que de fermantes. On s'interdit d'utiliser des variables qui "comptent" les parenthèses ouvrantes ou fermantes.

Par exemple

  • (..(..)..) est bien parenthésée.

  • (...(..(..)...) ne l'est pas .

Indication à ne pas dévoiler trop vite ...

On crée une pile. On parcourt l'expression de gauche à droite.
Si on rencontre une parenthèse fermante ")", alors :

  • Si la pile n'est pas vide, on dépile
  • Sinon on renvoie False

À la fin la pile doit être vide...

Implémentation du type Pile

Nous allons utiliser l'implémentation vue au II.
Pour qu'elle soit active, ne pas oublier d'exécuter "la classe Pile"

Les parenthèses

Compléter ci-dessous

###
# Testsbksl-nlassert verification("(3)py-str(2-5x)+3)") == Falsebksl-nlassert verification("(3)py-str(2-5x)+3") == Truebksl-nlbksl-nl# Autres testsbksl-nlassert verification("(3)py-str(2+6x)py-str(2-5x)+3)") == Falsebksl-nlassert verification("(3)py-str(2-5(2+x)x)+3") == Truebksl-nlbksl-nlbksl-nl 5/5
def verification(exprpy-undmath):bksl-nl """bksl-nl Cette fonction renvoie True si une expression mathématique a autant debksl-nl parenthèses ouvrantes que de parenthèses fermantes.bksl-nl Précondition : exprpy-undmath est de type chaîne de caractères.bksl-nl Postcondition : la valeur renvoyée est de type booléen.bksl-nl exemples :bksl-nlbksl-nl >>> verification("(3py-str(2-5x)+3)")bksl-nl Truebksl-nl >>> verification("(3py-str(2-5x)+3")bksl-nl Falsebksl-nl >>> verification("")bksl-nl Truebksl-nlbksl-nl """bksl-nl ...bksl-nlbksl-nlbksl-nl# Testsbksl-nlassert verification("(3)py-str(2-5x)+3)") == Falsebksl-nlassert verification("(3)py-str(2-5x)+3") == Truebksl-nlbksl-nlbksl-nlbksl-nlbksl-nldef verification(exprpy-undmath):bksl-nl """bksl-nl Cette fonction renvoie True si une expression mathématique a autant debksl-nl parenthèses ouvrantes que de parenthèses fermantes.bksl-nl Précondition : exprpy-undmath est de type chaîne de caractères.bksl-nl Postcondition : la valeur renvoyée est de type booléen.bksl-nl exemples :bksl-nlbksl-nl >>> verification("(3py-str(2-5x)+3)")bksl-nl Truebksl-nl >>> verification("(3py-str(2-5x)+3")bksl-nl Falsebksl-nl >>> verification("")bksl-nl Truebksl-nlbksl-nl """bksl-nl p = Pile()bksl-nl for car in exprpy-undmath:bksl-nl if car == '(':bksl-nl p.empiler(car)bksl-nl if car == ')':bksl-nl if p.estpy-undvidepy-undpile():bksl-nl return Falsebksl-nl else:bksl-nl p.depiler()bksl-nl return p.estpy-undvidepy-undpile()bksl-nlbksl-nlbksl-nlbksl-nlbksl-nl



Source : Stéphan Van Zuijlen