Université Mohammed V- Agdal Ecole Mohammadia d'Ingénieurs Département Génie Informatique Les Piles Définition - Une pile est une liste linéaire d d'éléments éléments (entiers, (entiers …)) où l'ajout et la suppression d'un élément se font du même côté. Sommet Structures de Données - Si la pile n'est pas vide, l'élément accessible est le dernier ajouté et qui se trouve au sommet de la pile Protocole LIFO: Last In First Out Exemple : Utilisation de la pile en informatique: • Journalisation des mises à jour pour réaliser la fonction “undo” (logiciels de bureautique p.e.) Par: Mr N.EL FADDOULI Année Universitaire:2013/2014 EMI/ Structures de Données / N. El Faddouli • Allocation de la mémoire pour les variables locales lors de l’exécution des procédures EMI/ Structures de Données / N. El Faddouli 33 1 Les Piles Les Piles - Les deux opérations principales sont: Exemple d'utilisation de pile: Empiler: ajoute un élément au dessus du sommet de la pile. Dépiler: supprime l'élément au sommet de la pile si elle n'est pas vide. Inverser une chaîne S Algorithme: P: Pile de caractères N Longueur(S) P Pour i 0 à N-1 N1 Empiler (S[i], P) Pour i 0 à N-1 S[i] Sommet(P) Depiler (P) - D'autres opérations sont possibles : Implémentation statique avec un tableau: InitPile : Cree une pile vide. - La pile est représentée par un enregistrement contenant les champs suivants: Sommet: Retourne l'élément au sommet de la pile - L'indice du sommet de la pile: un entier - Un tableau des éléments de la pile. qui doit être non vide. pile est vide ou non et PileVide: Détermine si la p - Il faut faire un contrôle de taille lors de l'empilement pour éviter un débordement (la pile ne doit pas être pleine) retourne Vrai ou Faux. EMI/ Structures de Données / N. El Faddouli - Ce tableau peut être statique (taille maximale fixée ) ou dynamique (réservation dynamique du tableau) . 34 EMI/ Structures de Données / N. El Faddouli 35 2 Les Piles Les Piles Implémentation statique avec un tableau: int Empiler (Pile *P, int X) 1 Pile avec un tableau statique: Pile d 1d'entiers entiers { if (PilePleine(*P)) return 0; typedef struct { int S; /* indice du sommet de la pile */ else { int T[100]; /* Tableau statique */ (*P).S ++; /* P->S ++; */ } Pile; Pil ((*P) P).T T [ ((*P) P).S S ] = X; //* P->T [ P->S ] = X ; *// return 1; Pile InitPile() { Pile P; P.S = -1; return P; } } } int PileVide(Pile P) { if (P.S == -1) return 1; else return 0; } int PilePleine(Pile P) { if (P.S == 99) return 1; int Depiler (Pile *P) else return 0;} { int Sommet(Pile P, P int *x) * ) { if ( ! PileVide(P) ) if (PileVide(*P)) (Pil Vid (*P)) return t 0 0; {*x = P.T[P.S]; return 1;} else { (*P).S -- ; /* P->S -- */ else return 0; return 1; } Il faut vérifier si la pile n'est pas vide avant de dépiler ou prendre le sommet de la pile. EMI/ Structures de Données / N. El Faddouli 36 } } EMI/ Structures de Données / N. El Faddouli 37 3 Les Files Les Files Définition - Les opérations sur les files sont: - Une file est une liste linéaire d d'éléments éléments où les insertions se font d'un côté et les suppressions de l'autre côté. Défiler InitFile: Cree une file vide. Tête : Retourne l'élément en tête de la file qui doit être non vide. Enfiler: Insère un élément à la fin de la file. file Enfiler Défiler: Supprime l'élément en tête de la file qui doit être non vide. FileVide: Teste si la file est vide et retourne vrai ou - Les noms spécifiques pour ces opérations sont Enfiler pour insérer et Défiler pour supprimer. faux Implémentation statique par un tableau: - Les opérations sur les files sont syntaxiquement les - Simplicité d'implémentation. mêmes que sur les piles ; c'est par leur effet qu'elles - Quand le nombre d'insertions est à priori limité. diffèrent: l'élément supprimé est le premier arrivé - Tableau contenant les éléments de la file. dans la file. - Deux indices, représentant respectivement le début - Une file se comporte comme une file d d'attente; attente; ( suit le protocole First In First Out (FIFO) ) - Opération supplémentaire: FilePleine qui détermine si - Exemple d'utilisation de file: Documents à imprimer, ... EMI/ Structures de Données / N. El Faddouli ett la l fin fi de d la l file. fil 38 une file est pleine. EMI/ Structures de Données / N. El Faddouli 39 4 Les Files Les Files int FileVide( File F) - Implémentation en langage C: { return ( F.Q F Q == 0); } typedef struct { int N; /* Nombre maximal d'éléments*/ int Tete( File F) int T, Q; /* tête et queue de file*/ { return (F.E [ F.T ] ); } int *E; E; //* représente les éléments de la file file*// int FilePleine( File F) } File; { return (F.Q == F.N) ; } Version 1: - La tête T de la file ne change pas (T = 0) - La queue Q de la file contient int Enfiler ((File *F,, int a)) l'indice { if (FilePleine (*F)) return 0; du dernier élément ajouté dans la file. { { File F ; F.N = a ; F->E [ F->Q ] = a; else File InitFile (int a) /* file de a élément*/ (*F).E[ (*F).Q ] = a; /* Taille maximale de la file*/ (*F).Q ++; F.T = 0; return 1; F Q = 0; F.Q } F->Q ++; } F.E = (int *) malloc( a * sizeof(int) ); return (F); } EMI/ Structures de Données / N. El Faddouli 40 EMI/ Structures de Données / N. El Faddouli 41 5 Les Files Les Files int Defiler (File *F) Version 2: File circulaire { - Éviter É le décalage des éléments au défilement. int i ; Exemple: File F de Taille maximale 3 if (FileVide (*F)) return 0; ( on prévoit 4 cases: Taille =4 ) else { /* Suppression du premier élément */ Après les opérations: Enfiler(F, ‘X’); T for (i=0; i<(*F).Q - 1; i++ ) (*F).E [ i ] = (*F).E [ i+1 ]; Q X ((*F) F).Q Q --;; Après les opérations: Enfiler(F, ‘Y’); Enfiler(F, ’Z’); return 1; T } Q } Le temps d'exécution de la fonction Defiler dépend du nombre d'élément de la file (peut être N éléments) Les autres fonctions ont un temps d'exécution Comment faire pour que le temps d'exécution de Defiler soit invariant lui aussi? (Voir la version 2) Y Z File pleine Après les opérations: Défiler( F) ; Enfiler(F, ‘R’); Q invariant. EMI/ Structures de Données / N. El Faddouli X T Y Z R File pleine 42 EMI/ Structures de Données / N. El Faddouli 43 6 Les Files Les Files typedef struct int Enfiler (File *F, int a) { int N; /* Nombre maximal */ int T, Q; /* tête et queue de file*/ int *E; /* représente les éléments de la file*/ { if (FilePleine (*F)) return 0; else { F-E [ ?F-Q ] = a;] = a; F-Q = ?(F-Q +1 ) % F-N ; } File; return 1; File InitrFile (int a) } { File F ; } F.N = a ; F.T = 0; F.Q = 0; int Defiler (File *F) F E = (int F.E (i t *) malloc( ll ( a * sizeof(int) i f(i t) ); ) { if (FileVide (*F)) return 0; return (F); } else { F-T = ?( ;F-T + 1) % F-N ; return 1; } int Tete(File F) } {return F.E [ F.T ]; } int FilePleine( File F) { if (F.T== (F.Q+1) % F.N) return 1; else return 0; } int FileVide( File F) { if ( F.T == F.Q ) return 1; else return 0; } EMI/ Structures de Données / N. El Faddouli 44 EMI/ Structures de Données / N. El Faddouli 45 7 Exercice 1 Exercice 1 - Pour traduire une expression arithmétique (infixe) en une expression en notation polonaise, il faut : Évaluation des expressions Postfixées 1- Convertir la partie la plus prioritaire - Notation infixe: l’opérateur se trouve entre les 2- La partie convertie est ensuite considérée comme un opérande. deux opérandes. Exemple: A+B. Exemple : (A+B)*C (AB+)*C AB+C* - Deux autres notations existent: (A+B)*(C-D) (A+B)(C-D)* AB+CD-* 1- La notation préfixée: l’opérateur est placé avant Si on considère l’opérateur d’exponentiation noté par $: les opérandes. Exemple: +AB. A$B*C-D+E/P/(G+H) ………………………………. postfixée (p (polonaise): ) l’opérateur p est 2- La notation p placé après les opérandes. Exemple: AB+. Remarques: - Pas de parenthèses dans les expressions postfixées. - On doit connaître la priorité des opérateurs. Quand on - L’ordre des opérateurs l’ordre des opérations. écrit A+B*C, c’est équivalent à: A+(B*C). - Pour convertir l’expression A+(B*C) en postfixée on Un opérateur U é t d dans une expression i postfixée tfi é s’applique aux deux opérandes qui le précèdent. effectue les étapes suivantes : - Ces opérandes peuvent à leur tour être le résultat de A+(B*C) A+(BC*) ABC*+ EMI/ Structures de Données / N. El Faddouli l’application d’un opérateur. 46 3 4 5 *+ = 3+20 = 23 34+5* = 7 * 5 = 35 EMI/ Structures de Données / N. El Faddouli 47 8 Exercice 2 Exercice 1 Algorithme général : On utilisera une pile pile, PileOperande On veut créer une file d’attente d’une imprimante. Cette Pour i 0 à Longueur(E) -1 file d’attente contient des fichiers à imprimer sachant Si E[ i ] est un opérande alors qu’un fichier est caractérisé par: Empiler (PileOperande, E[ i ] ) Un nom ( Nom ) Sinon Le nombre de page (NP) Sommet (PileOperande, val1 ) 1) Définir la structure Document représentant un Depiler (PileOperande) fichier. Sommet (PileOperande (PileOperande, val2 ) Depiler (PileOperande) 2) Définir la structure File représentant une file Res appliquer E[ i ] à val2 et val1 d’attente de fichier (document à imprimer). Empiler ( PileOperande, Res) 3)) Réécrire les fonctions: InitFile Fin Pour Tete Sommet (PileOperande, Resultat ) Enfiler Depiler(PileOperande) Remarque: Les autres fonction restent inchangées. Ecrire "Résultat Résultat de calcul de" de , E , " est " , Resultat T.A.F: Ecrire la fonction: float Calcul( char E[ ]) qui fonctions retourne le résultat de l’expression postfixée E. EMI/ Structures de Données / N. El Faddouli 4) Écrire un programme principal pour tester ces 48 EMI/ Structures de Données / N. El Faddouli 49 9 Les Listes chaînées Les Listes chaînées Un tableau: Opérations sur une liste chaînée: - Taille Fixe - Les éléments en mémoire sont contigus. C opérations Ces é ti sontt basées b é sur la l manipulation i l ti des d liens - Difficulté de redimensionner le tableau. - Simplicité d'ajout d'un élément.(ajout de 5 entre 8 et 1) - realloc permet de redimensionner un tableau avec 20 éventuellement un déplacement en mémoire de ses 6 8 1 éléments 7 5 realloc coûteuse en temps de traitement. Vs un tableau: Exemple: int *t =(int *) malloc (4 * sizeof(int)); …. Il t = (int *) realloc ( t, 20 * sizeof(int)); suivront la position d'insertion. Une liste chaînée: faut décaler à droite les cases qui - Simplicité de suppression ( supprimer 8 par exemple) - Un ensemble d'éléments non contigus organisés 20 séquentiellement. 6 8 1 5 7 Vs un tableau: 20 noeud 6 8 1 7 Il décaler à gauche les cases qui suivent l'élément à supprimer. lien EMI/ Structures de Données / N. El Faddouli faut 50 EMI/ Structures de Données / N. El Faddouli 51 10 Les Listes chaînées Les Listes chaînées Comment représenter les nœuds et les liens? Structure en C représentant un noeud - Un U nœud d estt une structure t t contenant: t t Exemple: Une liste d'entiers les données utilisateur (entier, réel, …). typdef struct Element (les éléments de la liste) { un pointeur ((lien)) vers le nœud suivant. int V ; 20 1000 V Suiv // une donnée utilisateur struct Element *Suiv; //pointeur sur l'élément suivant 6 } Nœud; Adresse = 1000 Une liste vide: - Le dernier nœud a un lien NULL L'initialisation de l'en-tête avec la valeur NULL permet 6 d'indiquer une liste vide. Exemple: - On mémorise l'adresse du premier nœud dans un void main() pointeur appelé l'en-tête de la liste chaînée. { Nœud *T; T 20 T T = NULL; …. …… } EMI/ Structures de Données / N. El Faddouli 52 EMI/ Structures de Données / N. El Faddouli 53 11 Les Listes chaînées Les Listes chaînées Insérer un élément au début d'une liste (2) Le champ Suiv du nouveau nœud doit pointer sur le l premier i nœud d de d la l liste. li t Nœud * InsererDebut ( Nœud *T, int X) T Le nouvel en-tête de la liste après ll'insertion insertion T Entier à insérer au début de la liste En-tête de la liste P 20 6 20 X 6 … … P->Suiv = T; Le nouvel en-tête de la liste est: P (1) Créer un nouveau nœud pointé par un pointeur P et contenant l'entier X. Nœud * InsererDebut ( Nœud *T, int X) { P Nœud * P = (Nœud *) malloc ( sizeof (Noeud) ); X Nœud *P; P->V=X; P = (Nœud *)) malloc ( sizeof (Noeud) ); P->Suiv = T;; return P; P->V = X; /* ou (*P).V = X EMI/ Structures de Données / N. El Faddouli */ } 54 EMI/ Structures de Données / N. El Faddouli 55 12 Les Listes chaînées Les Listes chaînées Exercice 1 Afficher une liste chaînée É Écrire un programme principal (main) ( ) permettant à void AfficherListe ( Nœud *T) l'utilisateur de saisir plusieurs entiers jusqu'à ce qu'il veuille s'arrêter. Chaque valeur saisie doit être insérer T au début d'un liste chaînée (appel de InsererDebut). En-tête de la liste 20 P 6 P … (1) Utiliser un pointeur P initialisé à T. (2) Tant Que P NULL - Afficher le champ V du nœud pointé par P. - Positionner P sur le nœud suivant - Revenir à (2) EMI/ Structures de Données / N. El Faddouli 56 EMI/ Structures de Données / N. El Faddouli 57 13 Les Listes chaînées Les Listes chaînées void AfficherListe ( Nœud *T)) { Nœud *P = T; while ( P != NULL) /* ou while ( P ) */ { printf("%d \t", P->V ); P = P->Suiv; } } void AfficherListe ( Nœud *T) { if ( T != NULL) { printf("%d \t", P->V ); AfficherListe (T->Suiv); } } Exercice 2 - Écrire et faire appel à une fonction récursive qui retourne le nombre d'éléments d'une liste chaînée. int Taille ( Nœud *T) EMI/ Structures de Données / N. El Faddouli 58 EMI/ Structures de Données / N. El Faddouli 59 14 Les Listes chaînées Les Listes chaînées Déterminer l'adresse du dernier noeud. Nœud * DernierNoeud ( Nœud *T)) Nœud * DernierNoeud ( Nœud *T) Adresse du dernier noeud { En-tête de la liste Nœud *P = T; if (T == NULL) return T; T while ( P->Suiv != NULL) 20 P 6 P … P = P->Suiv; 7 P /* ou while ( !P->Suiv ) */ retrun P; } (1) Utiliser un pointeur P initialisé à T. Cas Particulier (2) Tant Que P->Suiv NULL - Si la liste est vide: T == NULL - Positionner P sur le nœud suivant On ne peut pas parler de P->Suiv. - Revenir à (2) EMI/ Structures de Données / N. El Faddouli 60 EMI/ Structures de Données / N. El Faddouli 61 15 Les Listes chaînées Les Listes chaînées (2) Stocker dans un pointeur Q l'adresse du dernier noeud. Insérer un élément à la fin d'une liste T Nœud * InsererFin ( Nœud *T, int X) Le nouvel en-tête En-tête de la liste de la liste après l insertion l'insertion Q Entier à insérer à la fin de la liste 20 6 T P 20 6 … … 7 X (3) Faire le chaînage entre les deux nœuds pointés 7 respectivement par Q et P. P (1) Créer un nouveau nœud pointé par un pointeur P Q->Suiv = P ; Nœud * InsererFin ( Nœud *T, int X) et contenant l'entier X. { Nœud *P, *Q; P P = (Nœud *) malloc ( sizeof (Noeud) ); X P->V=X; P->Suiv = NULL; Nœud *P; if ( T == NULL) return P; P = (Nœud *)) malloc ( sizeof (Noeud) ); P->V = X; /* ou (*P).V = X Q = DernierNoeud( T ); Q->Suiv = P; */ return T; P->Suiv = NULL; EMI/ Structures de Données / N. El Faddouli } Cas particulier: Liste vide : T = NULL 62 EMI/ Structures de Données / N. El Faddouli 63 16 Les Listes chaînées Les Listes chaînées Supprimer le premier nœud: Nœud * SupprimerDebut pp ( Nœud *T)) Nœud * SupprimerDebut ( Nœud *T) Le nouvel en-tête de la liste après la suppression P T 20 { Nœud *P = T; En-tête de la liste if (T == NULL) return T; 6 T = T->Suiv ; … free ( P ) ; 7 retrun T ; (1) Mémoriser la valeur de T dans un pointeur P. } Nœud *P; Cas particulier P = T; - Liste vide : T == NULL (2) Positionner T sur le deuxième nœud. T = T->Suiv; ((3)) Libérer l'espace p occupé p p par le nœud p pointé p par P. free(P); (4) Retourner T . EMI/ Structures de Données / N. El Faddouli 64 EMI/ Structures de Données / N. El Faddouli 65 17 Les Listes chaînées Les Listes chaînées Supprimer le dernier nœud: Nœud * SupprimerFin pp ( Nœud *T)) Nœud * SupprimerFin ( Nœud *T) Le nouvel en-tête de la liste après la suppression { Nœud *P = T; En-tête de la liste P T if (T == NULL) return NULL; if (T->Suiv == NULL) { free( T ) ; return NULL; } 20 …... 9 while(P->Suiv->Suiv) 7 P = P->Suiv; P >Suiv; (1) Mémoriser la valeur de T dans un pointeur P. free ( P->Suiv ) ; Nœud *P; P->Suiv = NULL; P = T; retrun t T; (2) Positionner P sur l'avant dernier nœud. } (3) Libérer l'espace occupé par le dernier nœud. free(P->Suiv); Cas particuliers (4) Le L champ h S i du Suiv d nœud d pointé i té par P reçoitit la l - Liste vide : T == NULL valeur NULL. - Liste contenant un seul noeud : T->Suiv == NULL P->Suiv = NULL; EMI/ Structures de Données / N. El Faddouli 66 EMI/ Structures de Données / N. El Faddouli 67 18 Les Listes chaînées Les Listes chaînées Représentation dynamique d’une pile Représentation p dynamique y q d’une pile p 1. Définir une structure Pile représentant une liste d’entiers. 2 Ecrire 2. E i lles ffonctions ti suivantes: i t Pile * Empiler( Pile *P, int x) Pile * Depiler (Pile *P) int Sommet(Pile *P, int *x) int PileVide(*P) EMI/ Structures de Données / N. El Faddouli 68 EMI/ Structures de Données / N. El Faddouli 69 19 Les Listes chaînées Les Listes chaînées Représentation dynamique d’une pile Représentation p dynamique y q d’une file 1. Définir une structure Nœud représentant une liste d’entiers. 2 Définir 2. Défi i une structure t t Fil File contenant t td deux pointeurs i t de type Nœud. 3. Ecrire les fonctions suivantes: File InitFile( ) Créer une file dont les deux pointeurs sont égales à NULL. void Enfiler ( File *F, int x) void Defiler (File *F) int Tete(File *F, int *x) int FileVide(File *F) EMI/ Structures de Données / N. El Faddouli 70 EMI/ Structures de Données / N. El Faddouli 71 20 Les Listes chaînées Les Listes chaînées Représentation dynamique d’une file EMI/ Structures de Données / N. El Faddouli Représentation dynamique d’une file 72 EMI/ Structures de Données / N. El Faddouli 73 21