Cours BUT Info

Ch. 2 — Piles et files

Deux conteneurs qui ne laissent accéder qu'à un seul élément : la pile (dernier arrivé, premier sorti) et la file (premier arrivé, premier sorti) — parcours de graphes et calcul d'expressions.

Ce chapitre de R3.02 Développement efficace présente deux structures de données abstraites qui servent partout : la pile et la file. Leur particularité est contre-intuitive au début : contrairement à un tableau ou à une liste, on ne peut pas lire n'importe quel élément — un seul élément est accessible, toujours le même type de bout. C'est cette restriction qui les rend efficaces pour des problèmes précis : explorer un labyrinthe, gérer des annulations, dérouler les appels de méthodes, ou encore calculer des expressions arithmétiques.

1. Deux conteneurs, deux règles d'accès

La pileLa file
PrincipeLIFO — Last In First OutFIFO — First In First Out
L'élément accessiblele dernier ajouté (le sommet)le premier ajouté (la tête)
Imageun distributeur de bonbons PEZ®une file d'attente au magasin
On ajouteau sommetà la queue
On retireau sommetà la tête

2. La pile : dernier arrivé, premier sorti

Une pile est un conteneur d'éléments qui respecte le principe LIFO : l'élément accessible est le dernier élément ajouté, situé au sommet de la pile.

Une pile (LIFO) : on empile et on dépile toujours au même endroit — le sommet. Le dernier arrivé est le premier ressorti (comme un distributeur de bonbons PEZ).

Quelques exemples d'utilisation donnés par le cours :

  • la recherche de chemins dans un labyrinthe (c'est le TP 2 de cette ressource) ;
  • la gestion d'un historique de modifications (undo/redo : on empile les actions, on dépile pour annuler) ;
  • la gestion de la récursivité dans un langage : c'est la fameuse pile d'appels, qui retient « où revenir » quand une méthode en appelle une autre.

Le type de données abstrait (TDA) Pile

Un TDA décrit les opérations d'une structure sans dire comment elle est implémentée. Pour la pile, les opérations principales sont :

OpérationRôle
push(E val)ajoute un élément (au sommet)
E pop()retire et renvoie l'élément accessible (le sommet)
E peek()renvoie l'élément accessible sans le retirer
boolean isEmpty()teste si la pile est vide

Un seul point d'entrée

Tout se passe au sommet : on empile et on dépile au même endroit. C'est ce qui rend la pile si simple — et ce qui explique qu'on ne puisse lire que le sommet.

3. Application : le parcours en profondeur (avec une pile)

Un parcours de graphe consiste à visiter tous les sommets à partir d'une racine. Avec une pile, on obtient un parcours en profondeur : on part dans une direction et on va au bout de la branche avant de revenir.

La méthode : on empile les sommets visités ; à chaque étape, on dépile le sommet et on empile ses voisins (lus de gauche à droite). Le sommet de la pile est donc toujours le « prochain à explorer », et le dernier voisin empilé est examiné en premier.

Même graphe, deux conteneurs : avec une pile on descend au bout de chaque branche (A, E, C, G, B, F, H, D) ; avec une file on avance niveau par niveau (A, B, C, E, D, F, G, H).

4. Comment implémenter une pile ?

Une pile est un TDA : plusieurs implémentations donnent le même comportement.

  • à partir d'un tableau — c'est l'exercice 1 du TD : un tableau plus un indice sommet ;
  • à partir d'une liste chaînée ;
  • en Java, on utilise la classe Stack (synchronisée, un peu ancienne) ou, plus moderne, l'interface Deque (voir « File en Java » plus bas).

Exemple classique du cours : inverser une chaîne de caractères en empilant chaque caractère, puis en les dépilant — le dernier empilé ressort en premier, donc la chaîne est lue à l'envers.

Inverser.java
String input = "TRUC";
String output = "";
Stack<Character> stack = new Stack<Character>();
for (int i = 0; i < input.length(); i++) {
    stack.push(input.charAt(i));
}
while (!stack.isEmpty()) {
    output += stack.pop(); // C, puis U, puis R, puis T
}

5. La file : premier arrivé, premier sorti

Une file est un conteneur qui respecte le principe FIFO : l'élément accessible est le premier élément ajouté — le plus ancien de la file. On ajoute les éléments par la queue et on les retire par la tête, comme dans une file d'attente.

Une file (FIFO) : on ajoute à la queue et on retire à la tête, comme une file d'attente. Le premier arrivé est le premier servi.

Exemples d'utilisation : file d'attente dans un magasin, gestion de stocks de denrées périssables (on écoule les plus anciennes), ordonnancement de processus par un système d'exploitation.

Le TDA File

OpérationRôle
offer(E val)ajoute une valeur en queue de file
E poll()retire et renvoie la valeur en tête
E peek()renvoie la valeur en tête sans la retirer
int size()nombre d'éléments
boolean isEmpty()teste si la file est vide

6. Application : le parcours en largeur (avec une file)

Avec le même graphe que tout à l'heure, remplaçons la pile par une file : on retire le premier arrivé, et on ajoute ses voisins en queue. Le parcours change complètement de forme : on progresse niveau par niveau, d'abord tous les voisins directs de la racine, puis leurs voisins, etc. C'est le parcours en largeur.

Reprenez le schéma de la section 3 : avec une pile on visite A, E, C, G, B, F, H, D (on descend au bout des branches), avec une file A, B, C, E, D, F, G, H (niveau par niveau).

La question à se poser

« Est-ce que je dois aller au bout d'un chemin avant de tester les autres (pile), ou est-ce que je dois avancer progressivement sur toutes les pistes (file) ? » En TP, cette seule différence de conteneur change le chemin trouvé dans le labyrinthe.

7. La file en Java

Le TDA File est représenté en Java par l'interface Queue, qui étend Collection :

  • les implémentations principales : LinkedList et PriorityQueue (cette dernière exige des éléments Comparable, ou un Comparator à la construction) ;
  • les méthodes à connaître : offer (ajout en queue), peek (lire la tête), poll (retirer la tête).

Attention au vocabulaire Java

Les classes Java qui implémentent Queue (et Stack) contiennent beaucoup plus de méthodes que le TDA — parce qu'elles implémentent Collection. Ne vous laissez pas distraire : pour une file, offer / peek / poll ; pour une pile, push / peek / pop. (En Java moderne, Deque remplace avantageusement Stack.)

8. Application : les expressions arithmétiques

Le cours termine sur une application élégante des piles : le calcul d'expressions. Trois écritures existent pour 2 + 5 :

NotationÉcriture
infixée (habituelle)2 + 5
postfixée2 5 +
préfixée+ 2 5

La notation postfixée simplifie l'écriture : plus besoin de parenthèses. Par exemple (10+14)*(7-2) s'écrit 10 14 + 7 2 - *.

Convertir infixée → postfixée (avec une pile)

Les règles du cours :

  • un opérande (un nombre) : on l'ajoute directement à la sortie ;
  • un opérateur : on l'empile si la pile est vide ou contient une parenthèse ouvrante ; sinon on dépile d'abord tous les opérateurs de priorité égale ou supérieure (ajoutés à la sortie), puis on empile l'opérateur courant ;
  • une parenthèse ouvrante : on l'empile ;
  • une parenthèse fermante : on dépile jusqu'à avoir dépilé l'ouvrante, en ajoutant les opérateurs à la sortie ;
  • à la fin : on dépile tout le reste vers la sortie.

Suivons l'exemple du cours, (3+7)*(6-5)/((4-2)*(2+3)) :

On litPileSortie
( 3 + 7( +3 7
)(vide)3 7 +
* ( 6 - 5* ( -3 7 + 6 5
)*3 7 + 6 5 -
/ ( ( 4 - 2/ ( ( -3 7 + 6 5 - * 4 2
)/ ( *3 7 + 6 5 - * 4 2 -
( 2 + 3/ ( * ( +3 7 + 6 5 - * 4 2 - 2 3
)/ ( *… 2 3 +
)/… 2 3 + *
fin(vide)3 7 + 6 5 - * 4 2 - 2 3 + * /

Évaluer une expression postfixée (encore une pile)

Pour calculer une expression postfixée : on lit un élément à la fois ; un opérande est empilé ; un opérateur binaire dépile deux éléments, applique l'opération et empile le résultat. À la fin, la pile contient la valeur. Exemple du cours : 5 9 3 + 4 2 * * 7 + *

On litPileCalcul
5 9 35 9 3—
+5 129 + 3
4 25 12 4 2—
*5 12 84 × 2
*5 9612 × 8
75 96 7—
+5 10396 + 7
*5155 × 103

Piège de l'évaluation

Pour une opération non commutative (-, /), l'ordre compte : on dépile d'abord b (le sommet), puis a, et on calcule a - b (et non l'inverse). Une inversion et votre résultat est faux sans erreur de compilation…

A retenir

  • Pile = LIFO (dernier entré, premier sorti) — tout se passe au sommet : push, pop, peek, isEmpty.
  • File = FIFO (premier entré, premier sorti) — on ajoute en queue, on retire en tête : offer, poll, peek.
  • Le parcours en profondeur (au bout des branches) utilise une pile ; le parcours en largeur (niveau par niveau) utilise une file. Même graphe, résultat différent.
  • Une pile sert à inverser, à revenir en arrière et à calculer des expressions ; la conversion infixée → postfixée est l'exemple type du cours.
  • En Java : Stack (ancienne), Deque pour les piles modernes, Queue (LinkedList, PriorityQueue) pour les files.

cours2-piles-files.pdf

R3.02 · Cours 2 — Piles et Files — P. Everaere, F. Guyomarch · 106 diapositives

Consultation seule — téléchargement désactivé

Chargement du document…

Dernière mise à jour le

Sur cette page