Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Les Fils Et Les Piles OAII

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 10

EMIG OAII/S5 Structure de données - Les piles et les files

Les piles et les files


Nous avons découvert avec les listes chaînées un nouveau moyen plus souple que les tableaux
pour stocker des données. Ces listes sont particulièrement flexibles car on peut insérer et
supprimer des données à n'importe quel endroit, à n'importe quel moment.

Les piles et les files que nous allons découvrir ici sont deux variantes un peu particulières des
listes chaînées. Elles permettent de contrôler la manière dont sont ajoutés les nouveaux
éléments. Cette fois, on ne va plus insérer de nouveaux éléments au milieu de la liste mais
seulement au début ou à la fin.

Les piles et les files sont très utiles pour des programmes qui doivent traiter des données qui
arrivent au fur et à mesure.

Les piles et les files sont très similaires, mais révèlent néanmoins une subtile différence que
vous allez rapidement reconnaître. Nous allons dans un premier temps découvrir les piles qui
sont similaires aux listes chaînées, à quelques mots de vocabulaire près.

1 - Les piles

Imaginez une pile de pièces (fig. suivante). Vous pouvez ajouter des pièces une à une en haut
de la pile, mais aussi en enlever depuis le haut de la pile. Il est en revanche impossible d'enlever
une pièce depuis le bas de la pile.

1.1 - Fonctionnement des piles

Le principe des piles en programmation est de stocker des données au fur et à mesure les unes
au-dessus des autres pour pouvoir les récupérer plus tard. Par exemple, imaginons une pile de
nombres entiers de type int (fig. suivante). Si on ajoute un élément (on parle d'empilage), il
sera placé au-dessus (fig. suivante).

Chargé du cours : MIDOU Moussa Page 1 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

Le plus intéressant est sans conteste l'opération qui consiste à extraire les nombres de la pile.
On parle de dépilage. On récupère les données une à une, en commençant par la dernière qui
vient d'être posée tout en haut de la pile (fig. suivante). On enlève les données au fur et à mesure,
jusqu'à la dernière tout en bas de la pile.

On dit que c'est un algorithme LIFO, ce qui signifie « Last In First Out ». Traduction : « Le
dernier élément qui a été ajouté est le premier à sortir ».

Les éléments de la pile sont reliés entre eux à la manière d'une liste chaînée. Ils possèdent un
pointeur vers l'élément suivant et ne sont donc pas forcément placés côte à côte en mémoire.

Chargé du cours : MIDOU Moussa Page 2 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

Le dernier élément (tout en bas de la pile) doit pointer vers NULL pour indiquer qu'on a touché
le fond (fig. suivante).

À quoi est-ce que tout cela peut bien servir, concrètement ?

Il y a des programmes où vous avez besoin de stocker des données temporairement pour les
ressortir dans un ordre précis : le dernier élément que vous avez stocké doit être le premier à
ressortir.

Pour vous donner un exemple concret, votre système d'exploitation utilise ce type d'algorithme
pour retenir l'ordre dans lequel les fonctions ont été appelées. Imaginez un exemple :

1. votre programme commence par la fonction main ;


2. vous y appelez la fonction jouer;
3. cette fonction jouer fait appel à son tour à la fonction charger;
4. une fois que la fonction charger est terminée, on retourne à la fonction jouer;
5. une fois que la fonction jouer est terminée, on retourne au main;
6. enfin, une fois le main terminé, il n'y a plus de fonction à appeler, le programme
s'achève.

Pour « retenir » l'ordre dans lequel les fonctions ont été appelées, votre ordinateur crée une pile
de ces fonctions au fur et à mesure (fig. suivante).

Chargé du cours : MIDOU Moussa Page 3 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

Voilà un exemple concret d'utilisation des piles. Grâce à cette technique, votre ordinateur sait
à quelle fonction il doit retourner. Il peut empiler 100 fonctions d'affilée s'il le faut, il retrouvera
toujours le main en bas !

1.2 - Création d'un système de pile

Comme pour les listes chaînées, il n'existe pas de système de pile intégré au langage C. Il faut
donc le créer.

Chaque élément de la pile aura une structure identique à celle d'une liste chaînée :

typedef struct Element Element;


struct Element
{
int nombre;
Element *suivant;
};

La structure de contrôle contiendra l'adresse du premier élément de la pile, celui qui se trouve
tout en haut :

typedef struct Pile Pile;


struct Pile
{
Element *premier;
};

Nous aurons besoin en tout des fonctions suivantes :

• empilage d'un élément ;


• dépilage d'un élément.

Vous noterez que, contrairement aux listes chaînées, on ne parle pas d'ajout ni de suppression.
On parle d'empilage et de dépilage car ces opérations sont limitées à un élément précis,
comme on l'a vu. Ainsi, on ne peut ajouter et retirer un élément qu'en haut de la pile.

Chargé du cours : MIDOU Moussa Page 4 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

On pourra aussi écrire une fonction d'affichage de la pile, pratique pour vérifier si notre
programme se comporte correctement.

1.3 - Empilage

Notre fonction empiler doit prendre en paramètre la structure de contrôle de la pile (de
type Pile) ainsi que le nouveau nombre à stocker.
Nous stockons ici des int, mais rien ne vous empêche d'adapter ces exemples avec un autre
type de données. On peut stocker n'importe quel type de données : des double, des char,
des chaînes, des tableaux ou même d'autres structures.

void empiler(Pile *pile, int nvNombre)


{
Element *nouveau = malloc(sizeof(*nouveau));
if (pile == NULL || nouveau == NULL)
{
exit(EXIT_FAILURE);
}

nouveau->nombre = nvNombre;
nouveau->suivant = pile->premier;
pile->premier = nouveau;
}

L'ajout se fait en début de pile car, comme on l'a vu, il est impossible de le faire au milieu d'une
pile. C'est le principe même de son fonctionnement, on ajoute toujours par le haut.
De ce fait, contrairement aux listes chaînées, on ne doit pas créer de fonction pour insérer un
élément au milieu de la pile. Seule la fonction empiler permet d'ajouter un élément.

1.4 - Dépilage

Le rôle de la fonction de dépilage est de supprimer l'élément tout en haut de la pile. Mais elle
doit aussi retourner l'élément qu'elle dépile, c'est-à-dire dans notre cas le nombre qui était stocké
en haut de la pile.

C'est comme cela que l'on accède aux éléments d'une pile : en les enlevant un à un. On ne
parcourt pas la pile pour aller y chercher le second ou le troisième élément. On demande
toujours à récupérer le premier.

Notre fonction depiler va donc retourner un int correspondant au nombre qui se trouvait
en tête de pile :

int depiler(Pile *pile)


{
if (pile == NULL)
{
exit(EXIT_FAILURE);
}

int nombreDepile = 0;
Element *elementDepile = pile->premier;

if (pile != NULL && pile->premier != NULL)


{

Chargé du cours : MIDOU Moussa Page 5 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

nombreDepile = elementDepile->nombre;
pile->premier = elementDepile->suivant;
free(elementDepile);
}

return nombreDepile;
}

On récupère le nombre en tête de pile pour le renvoyer à la fin de la fonction. On modifie


l'adresse du premier élément de la pile, puisque celui-ci change. Enfin, bien entendu, on
supprime l'ancienne tête de pile grâce à free.

1.5 - Affichage de la pile

Bien que cette fonction ne soit pas indispensable (les fonctions empiler et depiler
suffisent à gérer une pile ), elle va nous être utile pour tester le fonctionnement de notre pile et
surtout pour « visualiser » le résultat.

void afficherPile(Pile *pile)


{
if (pile == NULL)
{
exit(EXIT_FAILURE);
}
Element *actuel = pile->premier;

while (actuel != NULL)


{
printf("%d\n", actuel->nombre);
actuel = actuel->suivant;
}

printf("\n");
}

Nous pouvons faire un main pour tester le comportement de notre pile :

int main()
{
Pile *maPile = initialiser();

empiler(maPile, 4);
empiler(maPile, 8);
empiler(maPile, 15);
empiler(maPile, 16);
empiler(maPile, 23);
empiler(maPile, 42);

printf("\nEtat de la pile :\n");


afficherPile(maPile);

printf("Je depile %d\n", depiler(maPile));


printf("Je depile %d\n", depiler(maPile));

printf("\nEtat de la pile :\n");


afficherPile(maPile);

return 0;

Chargé du cours : MIDOU Moussa Page 6 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

On affiche l'état de la pile après plusieurs empilages et une autre fois après quelques
dépilages. On affiche aussi le nombre qui est dépilé à chaque fois que l'on dépile. Le résultat
dans la console est le suivant :

Etat de la pile :
42
23
16
15
8
4

Je depile 42
Je depile 23

Etat de la pile :
16
15
8
4

Chargé du cours : MIDOU Moussa Page 7 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

2 - Les files

Les files ressemblent assez aux piles, si ce n'est qu'elles fonctionnent dans le sens inverse !

2.1 - Fonctionnement des files

Dans ce système, les éléments s'entassent les uns à la suite des autres. Le premier qu'on fait
sortir de la file est le premier à être arrivé. On parle ici d'algorithme FIFO (First In First Out),
c'est-à-dire « Le premier qui arrive est le premier à sortir ».

Il est facile de faire le parallèle avec la vie courante. Quand vous allez pour le paiement d’une
facture d’eau ou d’électricité, vous faites la queue au guichet (fig. suivante).

En programmation, les files sont utiles pour mettre en attente des informations dans l'ordre dans
lequel elles sont arrivées. Par exemple, dans un logiciel de chat (type messagerie instantanée),
si vous recevez trois messages à peu de temps d'intervalle, vous les enfilez les uns à la suite des
autres en mémoire. Vous vous occupez alors du premier message arrivé pour l'afficher à l'écran,
puis vous passez au second, et ainsi de suite.

En C, une file est une liste chaînée où chaque élément pointe vers le suivant, tout comme les
piles. Le dernier élément de la file pointe vers NULL (fig. suivante).

2.2 - Création d'un système de file

Le système de file va ressembler à peu de choses près aux piles. Il y a seulement quelques
petites subtilités étant donné que les éléments sortent de la file dans un autre sens.

Chargé du cours : MIDOU Moussa Page 8 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

Nous allons créer une structure Element et une structure de contrôle File :

typedef struct Element Element;


struct Element
{
int nombre;
Element *suivant;
};

typedef struct File File;


struct File
{
Element *premier;
};

Comme pour les piles, chaque élément de la file sera de type Element. À l'aide du pointeur
premier, nous disposerons toujours du premier élément et nous pourrons remonter jusqu'au
dernier.

2.3 - Enfilage

La fonction qui ajoute un élément à la file est appelée fonction « d'enfilage ». Il y a deux cas à
gérer :

• soit la file est vide, dans ce cas on doit juste créer la file en faisant pointer premier
vers le nouvel élément créé ;
• soit la file n'est pas vide, dans ce cas il faut parcourir toute la file en partant du premier
élément jusqu'à arriver au dernier. On rajoutera notre nouvel élément après le dernier.

Voici comment on peut faire dans la pratique :

void enfiler(File *file, int nvNombre)


{
Element *nouveau = malloc(sizeof(*nouveau));
if (file == NULL || nouveau == NULL)
{
exit(EXIT_FAILURE);
}

nouveau->nombre = nvNombre;
nouveau->suivant = NULL;

if (file->premier != NULL) /* La file n'est pas vide */


{
/* On se positionne à la fin de la file */
Element *elementActuel = file->premier;
while (elementActuel->suivant != NULL)
{
elementActuel = elementActuel->suivant;
}
elementActuel->suivant = nouveau;
}
else /* La file est vide, notre élément est le premier */
{
file->premier = nouveau;
}
}

Chargé du cours : MIDOU Moussa Page 9 sur 10


EMIG OAII/S5 Structure de données - Les piles et les files

Vous voyez dans ce code le traitement des deux cas possibles, chacun devant être géré à part.
La différence par rapport aux piles, qui rajoute une petite touche de difficulté, est qu'il faut se
placer à la fin de la file pour ajouter le nouvel élément. Mais bon, un petit while et le tour
est joué.

2.4 - Défilage

Le défilage ressemble au dépilage. Étant donné qu'on possède un pointeur vers le premier
élément de la file, il nous suffit de l'enlever et de renvoyer sa valeur.

int defiler(File *file)


{
if (file == NULL)
{
exit(EXIT_FAILURE);
}

int nombreDefile = 0;

/* On vérifie s'il y a quelque chose à défiler */


if (file->premier != NULL)
{
Element *elementDefile = file->premier;

nombreDefile = elementDefile->nombre;
file->premier = elementDefile->suivant;
free(elementDefile);
}

return nombreDefile;
}

Exercice
Ecrire une fonction afficherFile, comme fait pour les piles.

Réalisez ensuite un main pour exécuter le programme. Vous devriez pouvoir obtenir un
rendu similaire à ceci :

Etat de la file :
4 8 15 16 23 42

Je defile 4
Je defile 8

Etat de la file :
15 16 23 42

Source : www.openclassrooms.com

Chargé du cours : MIDOU Moussa Page 10 sur 10

Vous aimerez peut-être aussi