Académique Documents
Professionnel Documents
Culture Documents
Chargés de cours :
MM. ANIFRANI/GBODUI/TCHANTCHO
Semestre 2,
Année académique 2017-2018
Conception et Implémentation des Structures de Données
Plan du cours
1. GENERALITES
5. LES ARBRES
1.
GENERALITES
1.1. Problématique
Au début de l’informatique, l’accent a été mis essentiellement sur les traitements
c’est-à-dire l’aspect procédural des programmes aux dépens des données. Le
programmeur ne disposait pratiquement alors que de la case mémoire comme
élément de stockage et il devait exprimer totalement le schéma de résolution avec les
instructions de traitement présentes dans son langage de programmation.
L’ordinateur a été inventé pour exécuter plus facilement des calculs très longs et
complexes ; toutefois, dans la majorité des applications de l’informatique, c’est la
possibilité de stocker et de manipuler un grand nombre de données qui est
primordiale, tandis que la capacité à calculer, à exécuter des opérations
arithmétiques est très secondaire.
Lorsqu’on est dans ce cas, la grande quantité d’information que l’on doit traiter
représente, en un certain sens, une abstraction d’une partie du monde réel. Si l’on
veut résoudre un problème, même sans ordinateur, il est toujours nécessaire de
choisir une abstraction de la réalité, c’est-à-dire de définir l’ensemble des données que
représente la situation réelle. Ce choix doit être guidé par la nature même du
problème à résoudre. Vient alors le choix de la représentation de ces données, choix
lié à ce dont on dispose pour résoudre le problème, donc aux différents outils
disponibles sur l’ordinateur. Dans la plupart des cas, ces deux étapes ne sont pas
entièrement séparables.
Ces réflexions permettent de comprendre le choix de notations fait pour décrire dans
ce cours les structures de données et leurs implémentations. La prise en compte de
données complexes se révèle très fructueuse au niveau de la spécification du schéma
de résolution et ne doit pas se limiter aux seules structures présentes dans les langages
Page 1 sur 36
Conception et Implémentation des Structures de Données Généralités
1.2. Variable
Une variable est un symbole qui associe un nom appelé identifiant à une valeur. Les
variables constituent, avec les constantes, les données principales qui peuvent être
manipulées par un programme. La valeur d’une variable peut être de quelque type
de donnée que ce soit, et son nom doit être un identifiant unique (différent des mots-
réservés si le langage en possède).
En effet, une variable est un espace de stockage pour un résultat. Elle sert à
mémoriser de l’information. Ce qui est mis dans une variable est mis dans une partie
de la mémoire. Cependant, les possibilités d’une variable sont intimement liées au
langage de programmation auquel on fait référence. Par exemple, une variable en C
aura les caractéristiques ci-après :
- son nom ;
- son type ;
- sa valeur (si on utilise le mot-clé const, elle ne variera pas au cours du temps);
- son adresse ;
- sa portée (la portion de code source où elle est accessible) ;
- sa durée de vie (le temps d’exécution pendant laquelle la variable existe).
La notion de variable est complétée par la notion de pointeur (variable qui contient
l’adresse d’une autre variable. L’adresse contenue dans la variable est directement
accessible au programmeur en C/C++ mais peut ne pas l’être dans d’autres langages.
Page 2 sur 36
Conception et Implémentation des Structures de Données Généralités
Tout opérateur et toute fonction exige des arguments d’un certain type et produit
un résultat d’un type donné. Si un opérateur admet des arguments de types
différents, le type du résultat peut être déterminé à l’aide de règles spécifiques du
langage.
En conséquence, un compilateur peut utiliser cette information sur les types pour
vérifier la légalité de certaines instructions. C’est ainsi que l’affectation erronée d’une
variable booléenne à une variable arithmétique peut être détectée avant l’exécution
du programme.
On introduit des variables et des types de données dans un programme pour faire des
calculs ; c’est pourquoi on doit leur associer des opérateurs. Pour chaque type de
données standard, un langage de programmation propose un ensemble d’opérateurs
standard.
Les opérateurs de base les plus importants sont la comparaison c’est-à-dire le test sur
l’égalité (et sur l’ordre dans le cas des types ordonnés) et l’affectation c’est-à-dire
l’injonction à mettre à égalité.
Page 3 sur 36
Conception et Implémentation des Structures de Données Généralités
- Type abstrait : contient le nom du type que l’on est en train de décrire et précise
éventuellement si celui-ci n’est pas une extension d’un autre type abstrait ;
- Utilise : contient les types abstraits que l’on va utiliser dans celui qu’on est en
train de décrire ;
- Opérations : contient une description des opérations par leur nom, leurs
arguments et leur retour (prototypage) ;
- Pré-conditions : contient les conditions à respecter sur les arguments des
opérations pour que celles-ci puissent avoir un comportement normal.
- Axiomes : contient une série d’axiomes 1 pour décrire le comportement de
chaque opération d’un type abstrait.
1Un axiome est une proposition indémontrable utilisée comme fondement d’un raisonnement ou d’une théorie
mathématique.
Page 4 sur 36
2.
LES STRUCTURES DE
DONNEES STATIQUES
Une structure de données statique est un ensemble d’informations élémentaires liées
logiquement, dont le nombre est fixe. Si tous les éléments d’une structure sont de
même type, la structure est homogène, dans le cas contraire, elle est dite hétérogène.
- Spécification fonctionnelle
Le tableau monodimensionnel est un élément bien connu en mathématique ; c’est la
représentation informatique d’un vecteur. Cette structure est constituée d’un
ensemble de composantes repérables à l’aide d’une valeur appelé indice. Chaque
information élémentaire est accessible directement en lecture et en écriture. Cette
structure est extrêmement utilisée aussi bien en informatique que dans la vie
courante : on parle d’un tableau de notes, d’un tableau d’amortissement, etc.
- Spécification logique
Page 5 sur 36
Conception et Implémentation des Structures de Données Les structures de données statiques
En déclarant un tableau par int A[5] ; nous avons défini un tableau de 5 entiers
auxquels on peut accéder par A[0], A[1], …, A[4].
NB : Si on considère un tableau TAB de dimension N, en C l’accès au premier élément
se fait par TAB[0] et l’accès au dernier par TAB[N-1].
Ainsi, A[1] = 2 transfère la valeur 2 dans la case d’indice 1 (qui est la 2 ème case) du
tableau A.
- Spécification physique
Page 6 sur 36
Conception et Implémentation des Structures de Données Les structures de données statiques
- Spécification fonctionnelle
Les tableaux multidimensionnels sont des extensions des tableaux à une dimension,
servant à représenter des objets mathématiques plus complexes comme les matrices.
Ces structures qui apparaissent comme des tableaux dans un espace d’indices à
plusieurs dimensions sont utilisées très largement dans tous les domaines de la
physique, de l’informatique, etc. On peut citer par exemple la représentation d’une
grandeur physique en divers points de l’espace, le stockage d’informations pour des
dépouillements multicritères en statistique, etc.
- Spécification logique
La seule différence entre les tableaux mono et multidimensionnels est la méthode
d’adressage d’une composante, le traitement proprement dit de la composante ne
change pas, puisqu’il n’est pas caractéristique de la structure tableau mais seulement
du type de l’élément. Un élément de tableau multidimensionnel est repéré à l’aide de
plusieurs indices au lieu d’un seul mais on peut montrer facilement qu’un tel tableau
se ramène à une structure monodimensionnelle. En effet, un tableau
multidimensionnel n’est rien d’autre qu’un tableau de tableaux.
- Spécification physique
Comme pour les tableaux à une dimension, le nom du tableau représente l’adresse du
premier élément du tableau. En C, les composantes d'un tableau à deux dimensions
sont stockées ligne par ligne dans la mémoire : il s’agit de la représentation contiguë.
Dans d’autres langages comme Fortran, le stockage se fait plutôt colonne par
colonne.
Par ailleurs, il existe aussi une représentation chaînée dans laquelle on utilise une
hiérarchie de tableaux de pointeurs pour atteindre les éléments effectifs du tableau
considéré.
Page 7 sur 36
Conception et Implémentation des Structures de Données Les structures de données statiques
Trier c’est ordonner un ensemble d’éléments en fonction de clés sur lesquelles est
définie une relation d’ordre. Les méthodes de tri constituent un des domaines où le
plus d’études ont été effectuées pour trouver des algorithmes efficaces.
- Tri par échange : Le principe de base est de réordonner les couples non classés
tant qu’il en existe.
Partie triée
Page 8 sur 36
Conception et Implémentation des Structures de Données Les structures de données statiques
Tant que la partie non triée n’est pas vide, on en prend l’élément frontière avec la
partie triée et on l’insère à sa place dans cette dernière. L’insertion de l’élément
frontière est effectuée soit par décalages (dans ce cas, l’algorithme est itératif) soit
par permutations successives (algorithme récursif) à l’aide d’une procédure.
Cf. exercice d’application
Il existe une amélioration de ce tri appelée méthode de Shell. Elle ne fera pas l’objet
de ce cours.
Remarques :
1) Si après un parcours, l’élément maximal est rangé, le deuxième parcours peut
donc être limité à N-1 positions, etc. Une manière évidente d’améliorer cet
algorithme est donc de se souvenir qu’il y a eu ou non une permutation
pendant une passe et de se rappeler de la position de la dernière permutation.
Cet algorithme sera écrit en exercice d’application.
Page 9 sur 36
Conception et Implémentation des Structures de Données Les structures de données statiques
2) A cette amélioration, on peut ajouter une autre qui consiste à alterner le sens
du parcours dans le cas d’un tableau quasi-trié : c’est l’algorithme de Shakersort
ou alors le tri-shaker.
Ce tri a aussi donné lieu à deux améliorations dont la méthode du tournoi sportif qui
consiste à prendre en compte toutes les informations recueillies durant les différents
parcours afin de trier plus rapidement le tableau et la méthode du tri par arbre qui
consiste à considérer le tableau à trier comme la représentation d’un arbre binaire.
- Spécification fonctionnelle
Un enregistrement est un ensemble d’informations de types différents accessibles
individuellement ou collectivement en lecture et écriture. On trouve de très
nombreux exemples d’enregistrements dans la vie courante tels que l’identification
Page 10 sur 36
Conception et Implémentation des Structures de Données Les structures de données statiques
d’une personne pour la sécurité sociale qui comporte des informations aussi
différentes que le nom, le sexe, la date de naissance, le lieu de résidence, etc. De façon
générale, on peut représenter à l’aide d’un enregistrement toute collection
d’informations variées attachées à une personne ou un objet.
- Spécification logique
Pour spécifier au niveau logique un enregistrement nommé ENREG avec des champs
champ1, champ2, …, champn de types respectifs T1, T2, …, Tn, on utilise en C la
syntaxe suivante :
struct ENREG
{
T1 champ1;
T2 champ2;
...
Tn champn;
};
Exemple : On veut définir une structure Point permettant de stocker l’abscisse (x) et
l’ordonnée (y) d’un point.
struct Point
{
int x ;
int y ;
};
Les enregistrements peuvent contenir des tableaux. Soit une structure Personne :
struct Personne
{
char nom[20];
char prenom[20];
int age;
};
Le typedef
On peut ajouter une instruction appelée typedef qui sert à créer un alias c’est-à-dire à
dire qu’écrire telle chose équivaut à écrire telle autre chose.
typedef struct Personne Personne ;
Page 11 sur 36
Conception et Implémentation des Structures de Données Les structures de données statiques
Ainsi, pour déclarer une variable etudiant de type structure Personne, on va écrire
Personne etudiant ;
au lieu de
struct Personne etudiant ;
comme précédemment.
NB : Le typedef ne fonctionne pas qu’avec les structures. On peut l’utiliser avec les
types simples.
Exemple : typedef int Entier ;
- Spécification physique
Les enregistrements sont stockés en mémoire de manière contiguë avec ou sans
optimisation de place.
Exercices d’application
1) Ecrire un programme qui additionne deux matrices d’entiers de dimensions
NxP. Le résultat est stocké dans une 3ème matrice.
Page 12 sur 36
3.
LES STRUCTURES DE
DONNEES DYNAMIQUES
Exemple 1 : On doit lire sur un fichier d’entrée une suite de nombres sur lesquels on
effectuera un traitement ultérieur. On ne connaît pas la quantité de nombres à lire et
on ne peut les compter avant. Quelles sont les structures possibles pour représenter
l’ensemble de ces nombres ?
Le fait que le modèle de production soit récursif permet de construire en théorie des
expressions de taille infinie ; on ne peut donc pas envisager une structure de taille
fixe pour représenter de telles expressions, même si l’on sait que, dans la pratique, on
devra se limiter à une taille finie.
Page 13 sur 36
Conception et Implémentation des Structures de Données Les structures de données dynamiques
Exemple 3 : On veut modéliser une file d’attente de clients pouvant payer leur
facture au seul guichet d’une agence de la compagnie d’électricité ouvert le samedi…
a) La fonction malloc
malloc( <N> )
fournit l'adresse d'un bloc en mémoire de <N> octets libres ou la valeur zéro s'il n'y
a pas assez de mémoire.
Attention !
Sur notre système, le paramètre <N> est du type unsigned int. A l'aide de malloc,
nous ne pouvons donc pas réserver plus de 65535 octets à la fois!
Page 14 sur 36
Conception et Implémentation des Structures de Données Les structures de données dynamiques
Exemple :
Supposons que nous ayons besoin d'un bloc en mémoire pour un texte de 4000
caractères. Nous disposons d'un pointeur T sur char (char *T). Alors l'instruction :
T = malloc(4000);
fournit l'adresse d'un bloc de 4000 octets libres et l'affecte à T. S'il n'y a plus assez de
mémoire, T obtient la valeur zéro.
Si nous voulons réserver de la mémoire pour des données d'un type dont la grandeur
varie d'une machine à l'autre, nous avons besoin de la grandeur effective d'une
donnée de ce type.
Exemple 1 :
Après la déclaration,
short A[10];
char B[5][10];
Exemple 2 :
Nous voulons réserver de la mémoire pour X valeurs du type int; la valeur de X est
lue au clavier :
int X;
int *PNum;
printf("Introduire le nombre de valeurs :");
scanf("%d", &X);
PNum = malloc(X*sizeof(int));
Page 15 sur 36
Conception et Implémentation des Structures de Données Les structures de données dynamiques
c) exit
S'il n'y a pas assez de mémoire pour effectuer une action avec succès, il est conseillé
d'interrompre l'exécution du programme à l'aide de la commande exit (de stdlib) et de
renvoyer une valeur différente de zéro comme code d'erreur du programme
Si nous n'avons plus besoin d'un bloc de mémoire que nous avons réservé à l'aide de
malloc, alors nous pouvons le libérer à l'aide de la fonction free de la bibliothèque
stdlib.
free( <Pointeur> )
libère le bloc de mémoire désigné par le <Pointeur> n'a pas d'effet si le pointeur a la
valeur zéro.
Attention !
1) La fonction free peut aboutir à un désastre si on essaie de libérer de la mémoire
qui n'a pas été allouée par malloc.
2) La fonction free ne change pas le contenu du pointeur ; il est conseillé d'affecter
la valeur zéro au pointeur immédiatement après avoir libéré le bloc de
mémoire qui y était attaché.
3) Si nous ne libérons pas explicitement la mémoire à l'aide free, alors elle est
libérée automatiquement à la fin du programme.
On considère les élèves du cours primaire dans les rangs la main gauche posée sur
l’épaule de l’autre élève. Proposer une définition d’une structure Eleve de telle
manière qu’il soit possible de retrouver à chaque fois l’élève devant l’élève actuel.
Page 16 sur 36
Conception et Implémentation des Structures de Données Les structures de données dynamiques
struct Eleve
{
char nom[50];
char prenom[50];
int age;
struct Eleve *suivant; /*le pointeur sur le prochain élève*/
} ;
Exercices d’application
Page 17 sur 36
4.
LES LISTES CHAÎNÉES
On peut citer comme exemples d’une telle organisation : une file de voyageurs devant
un guichet, une file de voitures devant un poste de péage, etc. Nous appelons suite
une telle organisation dynamique homogène. Selon le mode d’exploitation d’une
suite, on définit différentes structures de données que nous allons présenter
maintenant.
Une liste chaînée est un ensemble ordonné et extensible d’éléments de même type
auxquels on accède séquentiellement, et où l’on peut ajouter ou retrancher un
élément en n’importe quelle position. C’est donc une suite sur laquelle on peut
effectuer une gamme très large d’opérations. Par exemple, un éditeur de textes utilise
une telle structure pour représenter un texte source qui apparaît comme une suite de
lignes dans laquelle on peut effectuer des insertions et des retraits en n’importe quel
point.
Page 18 sur 36
Conception et Implémentation des Structures de Données Les listes chainées
- Spécification logique
Une liste chaînée peut être décomposée en deux parties : le début de la liste et la suite.
On définira alors une liste chaînée d’éléments de type T de la façon suivante :
struct element {
T tete;
struct element* suivant; /* le pointeur sur le prochain élément */
};
En effet, suivant qui est le prochain élément sur la liste représente la suite de la liste.
Il suffira de placer à chaque fois un pointeur sur un élément d’une liste pour avoir la
liste à partir de cet élément. Il suffira donc de faire une déclaration de ce type :
struct element* listChainee
Pour simplifier l’écriture, on peut définir des types structure à l’aide de typedef.
Exemple : On veut définir une liste chaînée d’entiers.
typedef struct element {
int tete;
struct element* suivant;
} TElement;
- Spécification physique
Page 19 sur 36
Conception et Implémentation des Structures de Données Les listes chainées
NULL
Page 20 sur 36
Conception et Implémentation des Structures de Données Les listes chainées
NULL
TETE
NULL
TETE
Q P
On retire l’élément pointé par P. Mais pour cela, il faut s’assurer qu’il y ait toujours un
pointeur Q sur l’élément précédant celui pointé par P afin de ne pas perdre la liste.
1) Telles que nous les avons présentées, les listes chaînées ne peuvent être
parcourues que dans un seul sens. Quelquefois, il est intéressant pour traiter
certains problèmes d’effectuer des parcours dans les deux sens ; on définit alors
des listes doublement chaînées où chaque élément possède un lien de chaînage
vers l’élément suivant et un lien de chaînage vers l’élément précédent.
Page 21 sur 36
Conception et Implémentation des Structures de Données Les listes chainées
2) Une autre extension possible des listes chaînées consiste à poser que le premier
élément est le suivant du dernier : on définit ainsi une liste circulaire. On peut
aussi avoir des listes circulaires doublement chaînées.
- Spécification logique
Comme la liste, on peut décomposer une file d’attente en deux parties : d’une part la
tête qui correspond au premier élément (celui qui peut être retiré), d’autre part le
corps qui comprend tous les autres éléments. Cette décomposition met en évidence le
rôle particulier joué par l’élément de tête alors que l’élément de queue n’a pas besoin
d’être traité séparément car il ne joue aucun rôle dans le fonctionnement d’une file
d’attente.
Les primitives d’accès à une file d’attente permettent de retirer l’élément de tête
(fonction DEFILER), de rajouter un élément à la file (ENFILER) et de déterminer
si la file est vide (VIDE).
Ces fonctions peuvent être retrouvées à travers celles qui ont été écrites au niveau
des structures de liste chaînée. Il faut noter la primitive ENFILER est récursive.
Dans la pratique, il faut effectuer un test pour voir s’il y a de la place mémoire avant
d’effectuer ENFILER.
Page 22 sur 36
Conception et Implémentation des Structures de Données Les listes chainées
Cette structure correspond à la notion usuelle de pile d’assiettes, pile de dossiers, etc.
La caractéristique essentielle d’une pile est que l’on retire toujours d’abord le dernier
élément déposé, l’ordre de sortie étant donc l’inverse de l’ordre d’entrée. On utilise
fréquemment la structure de pile en informatique comme par exemple dans les appels
en cascade de sous-programmes, où l’on termine d’abord le sous-programme appelé
en dernier.
On peut définir une pile comme une structure de données dynamique homogène à un
seul point d’accès, les données sont ajoutées ou retranchées par l’intermédiaire d’une
tête d’accès appelé SOMMET de la pile. La gestion d’une pile est dite LIFO (Last In,
First Out) c’est-à-dire denier entré, premier sorti.
- Spécification logique
Au niveau logique, on décompose une pile en deux parties : le sommet et le corps. Les
opérations caractéristiques d’une pile sont EMPILER pour ajouter un élément et
DEPILER pour retirer un élément.
Remarque :
Au niveau physique, la file d’attente et la file peuvent être représentées de manière
chaînée ou de manière contiguë comme les listes chaînées.
Exercice d’application
On considère les élèves du cours primaire dans les rangs la main gauche posée sur
l’épaule de l’autre personne comme une liste chainée.
Page 23 sur 36
Conception et Implémentation des Structures de Données Les listes chainées
▪ Ecrire une fonction afficherListe qui permet d’afficher les objets d’une liste
passée en paramètre
▪ Ecrire une fonction ajouterEleveDebut puis une fonction
supprimerEleveDebut qui permet d’insérer ou de supprimer un élève en début
de la liste (premier élève de la liste)
▪ Ecrire une fonction ajouterEleveFin puis une fonction supprimerEleveFin qui
permet d’insérer ou de supprimer un élève à la fin de la liste (dernier élève de
la liste)
▪ Ecrire une fonction ajouterEleveDevantEleveX puis une fonction
supprimerEleveDevantEleveX qui permet d’insérer ou de supprimer un élève
devant l’élève X de la liste
▪ Ecrire une fonction ajouterEleveDerriereEleveX puis une fonction
supprimerEleveDerriereEleveX qui permet d’insérer ou de supprimer un élève
derrière l’élève X de la liste
Page 24 sur 36
5.
LES ARBRES
A *
+ F
B *
C -
D E
Page 25 sur 36
Conception et Implémentation des Structures de Données Les arbres
choix i
état initial
état i
Page 26 sur 36
Conception et Implémentation des Structures de Données Les arbres
On appellera tout naturellement feuille la racine d’un arbre n’ayant pas lui-même de
sous-arbres. Plus généralement, on appellera nœud la racine de tout sous-arbre, la
racine et les feuilles étant des nœuds particuliers. Un sous-arbre d’un arbre sera
appelé son fils et inversement celui-ci sera appelé père de celui-là. Deux sous-arbres
du même arbre seront des frères, et leurs racines respectives sont des nœuds frères.
Dans le cas d’un arbre binaire, la définition récursive se réduit à : un arbre binaire
est soit vide, soit formé d’une racine (RACINE) et de deux sous-arbres, le sous-arbre
gauche (SAG) et le sous-arbre droit (SAD) qui sont eux-mêmes des arbres.
Comme dit précédemment, la structure d’arbre n’est pas liée à des primitives de
manipulation caractéristiques, comme EMPILER et DEPILER pour les piles. En
fait, on distingue les arbres quelconques sur lesquels les seuls algorithmes usuels de
manipulation sont les parcours, des arbres ordonnés sur lesquels il peut être
intéressant de définir en plus des primitives standard de recherche, d’ajout et de
retrait d’un élément avec conservation de la propriété d’ordre qui qualifie l’arbre.
Page 27 sur 36
Conception et Implémentation des Structures de Données Les arbres
NB : Dans tous les cas, on dispose du prédicat logique VIDE(A) qui indique si l’arbre
A est vide ou non.
On appelle parcours d’un arbre tout algorithme permettant d’accéder une fois et une
seule à tous les nœuds de l’arbre. On distingue six parcours généraux, symétriques
deux à deux, valides pour des arbres binaires quelconques et répartis en trois
catégories qui sont les parcours préfixes ou en préordre, les parcours postfixes ou en
postordre et les parcours infixes ou en ordre.
Parcours préfixes
Faire un parcours préfixe d’un arbre binaire, c’est d’abord inspecter la RACINE puis
faire un parcours préfixe du SAG (resp. du SAD) et enfin faire un parcours préfixe
du SAD (resp. du SAG).
5 8
1 4 3
7 6
Page 28 sur 36
Conception et Implémentation des Structures de Données Les arbres
Parcours postfixes
Faire un parcours postfixe d’un arbre binaire, c’est d’abord faire un parcours postfixe
du SAG (resp. du SAD) puis du SAD (resp. du SAG) et enfin inspecter la RACINE.
Exemple : en considérant le même arbre que précédemment, on a :
Parcours postfixe SAG, SAD, RACINE : 7 1 4 5 6 3 8 2
Parcours postfixe SAD, SAG, RACINE : 6 3 8 7 1 4 5 2
La fonction ParcoursPostfixe sera écrite en exercice d’application
Parcours infixe
Faire un parcours infixe d’un arbre binaire, c’est faire un parcours infixe du SAG
(resp. du SAD) puis inspecter la RACINE et enfin faire un parcours infixe du SAD
(resp. du SAG).
Exemple : toujours avec le même arbre, on obtient dans ce cas :
Parcours infixe SAG, RACINE, SAD : 1 7 5 4 2 8 6 3
Parcours infixe SAD, RACINE, SAG : 3 6 8 2 4 5 7 1
La fonction ParcoursInfixe sera écrite en exercice d’application
Exemple :
Page 29 sur 36
Conception et Implémentation des Structures de Données Les arbres
13
9 45
5 11 50
7 10 12 48 52
Page 30 sur 36
Conception et Implémentation des Structures de Données Les arbres
8 8
4 10 4 10
5 9 13 5 9 13
0 0 0 0 0 0
6
0
3 9
2 5 11
4 6 10 19
17
Page 31 sur 36
Conception et Implémentation des Structures de Données Les arbres
4 9
2 5 11
6 10 19
17
16
7 18
4 9 24
2 6 8 10
16
6 18
4 9 24
2 8 10
Page 32 sur 36
Conception et Implémentation des Structures de Données Les arbres
NULL
On manipule l’arbre via un pointeur qui pointe sur la racine de l’arbre, qui elle-même
a deux pointeurs qui pointent sur ses nœuds fils et ainsi de suite
Exercices d’application
1. Ecrire la fonction vide qui retourne 1 si l’arbre est vide et 0 dans le cas
contraire.
2. Ecrire une fonction taille qui permet de calculer la taille d’un arbre. Cette
fonction calcule de façon récursive le nombre de nœuds de l’arbre. Si l’arbre est vide,
la fonction retourne 0. Si l’arbre contient au moins un nœud, la fonction retourne la
somme des tailles du SAG et SAD plus 1.
3. Ecrire une fonction hauteur qui permet de calculer la hauteur d’un arbre. Elle
la calcule de façon récursive. Si l’arbre est vide, hauteur retourne 0. Si l’arbre contient
au moins un nœud, hauteur retourne le maximum des hauteurs des sous-arbres plus
1.
4. Parcours en largeur : il s’agit ici d’un parcours de l’arbre couche par couche.
Page 33 sur 36
Conception et Implémentation des Structures de Données Les arbres
On part de l’idée suivante : pour parcourir l’arbre couche par couche, il faut essayer
de stocker les nœuds dans des couches. Plus précisément, on va maintenir pendant le
parcours deux couches : la "couche courante" qui contient les nœuds que l’on est en
train de parcourir, et la "couche des enfants" où on met les enfants de la couche
courante.
Un peu plus précisément, on a l’algorithme suivant :
• au départ, on met la racine dans la couche courante, on prend une liste
vide pour la couche des enfants ;
• ensuite, on parcourt les nœuds de la couche courante en ajoutant leurs
enfants dans la couche des enfants ;
• quand on a terminé le parcours, on change les couches : on prend la
couche des enfants comme une nouvelle couche et on recommence le
parcours.
Quand, à la fin du parcours de la couche courante, on obtient une couche des enfants
vide, l’algorithme s’arrête. En effet, s’il n’y a pas d’enfant dans la couche des enfants,
cela veut dire qu’aucun des nœuds de la couche qui vient d’être parcourue n’avaient
d’enfants : ce n’était que des feuilles, donc on est arrivé à la fin de l’arbre.
Page 34 sur 36
Références bibliographiques
J. Guyot & C. Vial, Arbres, Tables et Algorithmes, Eyrolles, Paris, 1988.
N. Wirth, Algorithmes et Structures de Données, Eyrolles, Paris, 1988.
F. Faber & J.F. Anne, Programmation en C, Document numérique.
www.openclassrooms.com
Page 35 sur 36
Table des matières
1. GENERALITES...................................................................................................... 1
1.1. Problématique .................................................................................................... 1
1.2. Variable .............................................................................................................. 2
1.3. Type de données ................................................................................................. 2
1.4. Structure de données ........................................................................................... 3
1.5. Type abstrait de données .................................................................................... 4
1.6. Structure de données et type abstrait de données ................................................ 4
2. LES STRUCTURES DE DONNEES STATIQUES ................................................ 5
2.1. Structures de données statiques homogènes ........................................................ 5
2.1.1. Tableau monodimensionnel .......................................................................... 5
2.1.2. Tableau multidimensionnel .......................................................................... 7
2.1.3. Les méthodes de tri ...................................................................................... 8
2.2. Structure de données statique hétérogène : enregistrement ................................10
3. LES STRUCTURES DE DONNEES DYNAMIQUES ...........................................13
3.1. Emergence du besoin de structures dynamiques .................................................13
3.2. Représentation physique des structures dynamiques .........................................14
3.2.1. La fonction malloc et l’opérateur sizeof .......................................................14
3.2.2. La fonction free ...........................................................................................16
3.3. Les enregistrements récursifs .............................................................................16
4. LES LISTES CHAÎNÉES ......................................................................................18
4.1. Notion de suite ..................................................................................................18
4.2. Les listes chaînées ..............................................................................................18
4.3. Les files d’attente...............................................................................................22
4.4. Les piles .............................................................................................................23
5. LES ARBRES ........................................................................................................25
5.1. Notion d’arbre ...................................................................................................25
5.2. Spécification fonctionnelle .................................................................................26
5.3. Spécification logique des arbres binaires.............................................................27
5.3.1. Définitions ..................................................................................................27
5.3.2. Primitives d’accès .......................................................................................27
5.3.3. Parcours d’arbres binaires ...........................................................................28
5.3.4. Arbres ordonnés ..........................................................................................29
5.4. Spécification physique .......................................................................................33
Références bibliographiques ............................................................................................35
Page 36 sur 36