Partie 2: Les structures de données
Chapitre3:
1
A: Les piles 1/ Définition: La pile est une liste ordonnée d’éléments dans laquelle on ne peut introduire introduire ou enlever un élément qu'à une extrémité. Cette Cette extrémité est appelée tête de pile ou sommet de pile.
e t ê t a F l à t i E a r t e r : r e l i p é d
D
Toutes les opérations sont effectuées sur la même extrémité =>LIFO. Ex: Dans un navigateur, navigateur, une pile sert à mémoriser les pages Web visitées. L'adresse de chaque nouvelle page visitée est empilée. L'utilisateur dépile en cliquant le bouton« page
C
B A
e t ê t a l à t u o j a : r e l i p m E
A: Les piles 1/ Définition: La pile est une liste ordonnée d’éléments dans laquelle on ne peut introduire introduire ou enlever un élément qu'à une extrémité. Cette Cette extrémité est appelée tête de pile ou sommet de pile.
e t ê t a F l à t i E a r t e r : r e l i p é d
D
Toutes les opérations sont effectuées sur la même extrémité =>LIFO. Ex: Dans un navigateur, navigateur, une pile sert à mémoriser les pages Web visitées. L'adresse de chaque nouvelle page visitée est empilée. L'utilisateur dépile en cliquant le bouton« page
C
B A
e t ê t a l à t u o j a : r e l i p m E
2/ Opérations Opé rations courantes: Empi ler,, Dépiler Dé piler.. A/ Les pilesEmpiler Une pile est une liste pour laquelle on peut : • Ajouter un élément au début = empiler l'élément = le mettre au sommet de la pile ==> fonction PUSH • Supprimer un élément du début = dépiler l' élément = l'enlever du sommet ==> fonction POP vide,… etc • tester si la pile est vide,…etc MAIS à laquelle laquelle on ne peut: peut: • Ni ajouter un élément à la fin; ni au milieu; un élément à la fin; fin; ni du au milieu. • Ni supprimer un
!
Il n’y a pas de limite au nombre d’éléments qu’on peut empiler pour une implémentation par liste chaînée. Par contre, contre, on ne peut dépiler d’une pile vide. EX: Utiliser une fonction fonction PileVide(p) qui retourne retourne VRAI si la pile est vide sinon FAUX.
3/ Insertion/suppression d'un élément à la fin/milieu On procède par une succession de dépilement/empilement … Ex: Suppression de l'entier 5 d'une pile d'entiers 7 6
6
5
5
5
4
4
4
3
3
2 1
Dépiler
7 6
6
4
4
4
3
3
3
3
2
2
2
2
2
1
1
1
1
1
Dépiler
Dépiler
Empiler
Empiler
4/ Implémentation d'une pile Une pile est une liste + Une liste s'implémente de 2 façons=> 2 façons pour la pile aussi
Une représentation par un tableau: • Stockage contigüe dans la mémoire • taille maximale définie d'avance. • Une variable supplémentaire pour stocker l'indice du sommet, … Une représentation par liste chaînée: • taille de la pile peut être modifiée • Le maillon possède un lien vers l'élément précédent, • Un pointeur sur le premier élément désigne la pile et représente le sommet de cette pile • Une pile vide est représentée par le pointeur NULL , …
Structure mapile{ Maillon typique
type1 nomvariable1; type2 nomvariable2; …. structure mapile *précédent; } ;
5/ Ex. d'opérations courantes sur les piles Fonction
Argument(s)
Retour
Pilevide : teste si une pile est vide
Pile
Booléen
sommet: permet de consulter l'élément situé au sommet ! n'a pas de sens si pile vide
Pile
Elément
empiler : ajoute un élément dans la pile
Pile , Elément
Pile
dépiler : enlève l'élément situé au sommet. ! n'a pas de sens si pile vide
Pile
Pile
•Elément peut être un entier, une chaîne, une structure, …. selon la définition du maillon qui constitue la pile •Les algorithmes sont des exemples, et ne sont pas uniques. Ils sont à adapter au modèle de structure énnoncé dans vos exercices
6/ Consulter le sommet d'une pile
Ex: Ecrire l'algorithme d'une fonction sommet, retournant la valeur du sommet d'une pile d'entiers
Algorithme fonction sommet E: structure maillon * mapile S: entier R ----------------------------------Début Si (mapile == NULL) afficher (rien à afficher!); retourner NULL ; Sinon R = mapile->contenu; retourner R; FinSI Fin
7/ Fonction empiler Principe •Créer un nouvel élément , •y mettre les données. •Faire pointer ce nouvel élément sur la pile. •Retourner le pointeur sur ce nouvel élément
Pour un maillon de type:
structure maillon { entier contenu; structure maillon * précédent; };
Analogue à Ajouter en tête d'une liste chaînée
Algorithme fonction empiler E: structure maillon *p, entier x S: structure maillon *p ------------------------------------------Début structure maillon * nv; nv->contenu=x;
La fonction empiler serait = >
nv->précédent=p; retourner nv; Fin
8/ Fonction dépiler (avec retour de l'adresse du nv sommet)
Algorithme fonction dépiler E: structure maillon *p S: structure maillon *p ------------------------------------------Début Si (p=NULL)alors Afficher("rien à dépiler!"); retourner NULL; sinon structure maillon * t = p; Question: Est-il nécessaire de faire une p=p->precedent; copie du sommet (i)? Libérer(t); retourner p; Finsi 9/ Fonction ViderPile Fin Déduire l'algorithme d'une fonction qui permet de vider toute la pile. Principe •Vérifier que la pile n'est pas vide •Copier le sommet dans une variable auxiliaire •Faire pointer le pointeur pile sur l'avant dernier élément •Libérer le sommet
Exercice: Pour une pile d'entiers, traduire en langage c les fonctions pilevide, sommet, dépiler, empiler.
struct maillon { int valeur; struct maillon *precedent; };
mapile
typedef struct maillon maillon; 10
5
20
typedef maillon *Pile;
50
NULL
Les (typedef) ne sont pas obligatoires!
Des exemple de solutions….
int pilevide(Pile p){
}
if (p==NULL) return 0; else return 1;
Pile empiler(Pile p, int e) { Cellule * pc; pc=(maillon *)malloc(sizeof(maillon)); pc->valeur=e; pc->suivant=p; return pc; }
int sommet(Pile p) { if ( pilevide(p)) printf("Erreur: pile vide !"); … else return p->valeur; }
Pile depiler(Pile p) { if ( pilevide(p)) { printf("rien à dépiler!\n"); return NULL; } else { maillon * pc = p; p=p->precedent; free(pc); return p; }}
Ne pas oublier de créer le tout premier pointeur avant d'essayer d'empiler …
// définition du modèle de structure … //définition des fonctions précédentes ….. main () { Pile mapile = NULL;
Faut créer "la premiere brique" Quel est le type de mapile?
mapile = empiler(mapile,50); mapile = empiler(mapile,5); mapile = empiler(mapile,20); printf("%d est au sommet\n", sommet(mapile)); mapile = depiler(mapile); mapile = depiler(mapile); printf("%d est au sommet\n", sommet(mapile)); }
B/ Les files 1/ Définition: Une file est une liste dans laquelle on insère des nouveaux éléments à la fin (queue) et où on enlève des éléments au début (tête de file). A
B
défiler : retrait à la tête
C
D
E
F
Enfiler: ajout à la fin
le premier élément inséré est aussi le premier retiré.
Accès FIFO (First In Fist Out).
2/ex. d'opérations courantes sur les files: Enfiler, défiler A/ Les piles Une file est une liste pour laquelle on peut par exemple: • Ajouter un élément à la fin = enfiler l'élément = le mettre à la fin de la file • Supprimer un élément du début = défiler l' élément = l'enlever de la tête • Tester si la file est vide, … MAIS à laquelle on ne peut: • • • •
Ni ajouter un élément au début; Ni ajouter un élément au milieu; Ni supprimer un élément à la fin; Ni supprimer un élément au milieu.
y a-t-il une limite au nombre d’éléments qu’on peut enfiler/ défiler?
!
Comme pour les piles, l'insertion/suppression => une succession d'opérations défiler/enfiler.
3/Implémentation Implémentation d'une des files 3/ file Une file est une liste + Une liste s'implémente de 2 façons=> 2 façons pour la file aussi
Une représentation par un tableau • Les éléments de la file sont rangés dans un tableau • Stockage contigüe sur la mémoire. • Deux entiers représentent les positions de la tête/la queue de la file Une représentation par liste chaînée: • Le maillon possède un lien vers l'élément suivant, • Un pointeur tête pointant sur le premier élément désigne la file et représente la tête de cette file • Un pointeur queue pointant sur le dernier élément représente la queue de cette file les maillons chaînés
+
Il faudra donc: pointeur sur la tête +pointeur sur la queue Peuvent être groupés dans une structure
Implémentation d'une file (suite) Les algorithmes qui suivront sont écrits pour le maillon ci dessous. Les maillons tête et queue sont groupés dans une structure File.
structure maillon { entier contenu; structure maillon * suivant; }; structure File { structure maillon *tete; structure maillon *queue; }; File f;
f n'est pas un pointeur On écrira f tete=... f queue
Un maillon contient un élément et un pointeur sur l'élément suivant, il constituera la file
type File : structure à deux champs (pointeurs de tête et de queue de file) N'appartient pas à la
file elle-même, Faudra mettre à jour les valeurs contenues dans cette structure à chaque ajout/retrait
4/ Ex. d'opérations courantes sur les piles Fonction
Argument(s)
Retour
file
Booléen
tête: permet de consulter l'élément situé file en tête de file ; ! n'a pas de sens si la file vide
Elément
filevide: teste si une file est vide
enfiler : ajoute un élément à la file
file , Elément
file
défiler :enlève l'élément situé à la tête. ! n'a pas de sens si file vide
file
file
Tout comme pour les piles: •Elément peut être un entier, une chaîne, une structure, …. selon la définition du maillon qui constitue la file •Les algorithmes sont des exemples, et ne sont pas uniques.
5/ Fonction Créer une file vide On ne crée pas les maillons. Seule la structure File (tête+queue ) est à initialiser
Algorithme fonction creefilevide E: Rien S: structure File f ------------------------------------Début //crée File f; f.tete=NULL; //initialise f.queue=NULL; retourner f; fin
6/ Fonction filevide Une file est vide est si les deux pointeurs tête et Queue sont NULL.
Algorithme fonction filevide E: structure File f S: entier. ----------------------------------------------Début Si (f.tete=NULL) et (f.queue=NULL) alors retourner 0; Sinon retourner 1 Finsi fin
7/ Fonction enfiler Algorithme fonction enfiler E: structure File f, entier x S: structure File f Début structure maillon * nv;
//Créer un nouvel élément ,
nv->contenu=x;
//Y mettre les données.
nv->suivant=NULL;
//pointer sur NULL puisqu'il devient le dernier.
si(filevide(f))
// vide tète=queue=cet élément unique
f.tete=f.queue=nv; Sinon (f.queue)->suivant=nv;
//le dernier devient avant dernier, avant nv
f.queue=nv;
//mise à jour: queue est maintenant nv
Finsi retourner f;
8/ Fonction défiler (avec retour de l'adresse du nv sommet)
Algorithme fonction défiler E: structure File f, S: structure File f Début Si (filevide(f)) alors Afficher("rien à défiler!"); retourner NULL; sinon Structure maillon * tmp; tmp=f.tete; f.tete=(f.tete) -> suivant; libérer (tmp); si (f.tete=NULL) alors f.queue=NULL; finsi retourner f ; Finsi; Fin
//on copie la tete // la tête est maintenant le second élément
// et si ce maillon défilé était le seul dans //cette file? // faudrait mettre queue à NULL aussi //retourner f puisqu'elle fut modifiée
C/ LES ARBRES 1/ Définition d'un arbre: un arbre est un graphe acyclique orienté • possédant une unique racine, et •tel que tous les nœuds -sauf la racine- ont un unique parent. •Un arbre est une structure arborescente composée d’éléments appelés nœuds ; •Chaque nœud est constitué : – d’un champ de donnée ; – d’un ou plusieurs pointeurs vers des nœuds. •Un arbre est donc une racine et une suite (éventuellement vide) de sous arbres A1, …, An. •Il existe un unique chemin allant de la racine a n'importe quel nœud de l'arborescence. La donnée du pointeur sur le nœud racine défini tout l'arbre , tout comme la donnée de l'adresse de la tête d'une liste chaînée définit toute la liste
2/ Terminologie… •un noeud interne est un nœud qui a au moins un fils •une feuille d’un arbre est un nœud sans fils ; dite aussi une extrémité; •les ancêtres d’un nœud a sont les nœuds qui sont sur le chemin entre la racine (incluse) et a (inclus) ; •les descendants d’un noeud a sont les nœuds qui appartiennent au sous-arbre de racine a ; Ex:
Les feuilles de l'arbre à côté sont: Les nœuds internes sont:
Les ancêtres de 13 sont: Les ancêtres propres de 13 (càd 13 non inclus) sont:
•la profondeur d’un nœud est la longueur du chemin allant de la racine à ce nœud Longueur désigne les liens et non pas les nœuds
La profondeur de la racine est donc nulle la profondeur d’un nœud autre que la racine vaut =1+ la profondeur de son père;
•Un niveau est un ensemble de nœuds internes ou de feuilles situés à la même profondeur (5 et 20 par ex) •la hauteur d’un arbre: profondeur maximum des nœuds ; elle vaut dans cet exemple 4 Un arbre est dit équilibré, si les les sous-arbres gauche et droit ont des hauteurs qui diffèrent au plus de 1
3/ Arbre binaire de recherche (un exemple d'arbres) Conditions: •les nœuds appartiennent à un ensemble totalement ordonné. •Binaire => Chaque nœud a au plus 2 nœuds les données contenues dans le sous arbre gauche de tout nœud interne a sont inférieures ou égales à la donnée contenue dans le nœud a lui même, qui est à son tour inférieure ou égale aux données contenues dans le sous-arbre droit de a.
2 <5
5 <10
16 <20
Fils Gauche < nœud < fils droit
Ex: est ce un arbre binaire de recherche?
Cet arbre est il binaire? Si oui, est ce un ABR? Est il équilibré?
Fils Gauche < nœud < fils droit
4/ Implémentation d'un arbre (ex.) La structure élémentaire utilisée pour construire l'arbre peut être l'une des suivantes:
•Les données (valeurs contenues dans le nœud ), •Un lien vers chaque nœud fils, Un arbre vide permet de caractériser les feuilles. •Les données •Un lien vers le premier nœud fils (fils gauche généralement), •Un lien vers le nœud frère ( premier nœud frère sur la droite). •Les données •Un lien vers le nœud père, puisque chaque nœud possède UN père. TAF:
Dessiner un schéma illustratif des ces trois exemples
5/ Implémentation d'un arbre (Exemple) Structure noeud { entier contenu; Structure nœud Structure nœud };
* fils_gauche; * fils_droit;
structure nœud * arbre ;
arbre->contenu=2; arbre->fils_gauche->contenu=7; arbre->fils_droit->contenu=5; arbre->fils_gauche->fils_gauche->contenu=2; arbre->fils_droit->fils_gauche->contenu=8; arbre->fils_gauche->fils_droit->contenu=6; arbre->fils_droit->fils_droit->contenu=3;
Schématiser cet arbre. Est-ce un ABR? Est un arbre équilibré?
6/ Exemple d'opérations: la Taille de l’arbre Ecrire une fonction récursive taille qui calcule le nombre de nœuds d’arbre. Si l’arbre est vide, la fonction retourne 0. Si l’arbre contient au moins un noeud, la fonction retourne la somme des tailles du sous-arbre gauche et du sous-arbre droit plus 1
Algorithme fonction taille E: structure nœud * arbre
S: entier ------------------------------------- Début structure nœud * t = arbre;
Si(t=NULL) alors Retourner 0 Sinon r etour ner 1 + taille(t->fils_gauche) + tail le(t->fi ls_dr oit);
Finsi fin
7/ Parcours d'un arbre Le parcours d'un arbre peut se faire de différentes façon. • En largeur • En profondeur a. En préfixe b. En suffixe c. En infixe
Parcours en largeur Le parcours en largeur correspond à un parcours par niveau de nœuds de l'arbre. = > 2-7-5-2-6-9-5-11-4
RMQ: Ces exemples -valables pour un arbre binaire- sont à généraliser au cas d'arbres n-aires …
Parcours en profondeur Le parcours en pré ordre, dit aussi en ordre préfixe. · Lire la racine ; · parcourir en pré ordre le premier sous-arbre;…. · parcourir en pré ordre le dernier sous-arbre. => 2 7 2 6 5 11 5 9 4
:racine en premier
Le parcours en post ordre, dit aussi en ordre suffixe: · parcourir en le premier sous-arbre ;….. · parcourir en le dernier sous-arbre ; · Lire la racine. => 2 5 11
:racine en dernier
6 7 4 9 5 2
Parcours en ordre infixe:
Traiter le fils gauche, le nœud courant , puis le fils droit.. => 2
7
5
6 11
2
5
4 9