1 Arbres
Arbre binaire (AB)
Représentation SDD d’un AB
Algorithmes de parcours d’un AB
Arbre binaire de recherche (ABR)
…
Exemples
2
Naturels
Organigramme d’une entreprise
Résultat d’un tournoi
Informatiques 3
Système de fichiers
Langages et compilation 2 1
Arbre de dérivation
Arbre d’analyse
3 4 5
Traitements récursifs
…
Utilisation
3
Structure fondamentale de
l’informatique
Définition et traitement naturellement
récursifs
En précisant une relation d’ordre sur les
éléments
Arbre de recherche
Définition (théorie des graphes)
4
Arbre
Arbre
graphe connexe acyclique
Arbre binaire
arbre dont le degré des nœuds est au plus 3
Ce sont des graphes non orientés
Arbre binaire enraciné
On précise arbitrairement un nœud en tant que
racine
Parmi les nœuds de degré au plus deux
L’orientation (relation parent-enfant) est alors
induite
Définition Arbre
5
binaire(SDD)
Ensemble hiérarchisé de nœuds
Containers d’élément
Un nœud racine unique ou inexistant (arbre vide)
Chaque nœud non racine possède un unique
parent
Chaque nœud peut posséder deux enfants
Un fils gauche et un fils droit
A noter
On reprend la définition formelle
arbre binaire enraciné
On précise deux types de relations parent-enfant
Soit à droite, soit à gauche
Qualification des nœuds
6
Par transitivité de la relation parent-enfant
Ancêtre ou ascendant vs. descendant
Propriété : la racine est l’ancêtre de tous les
nœuds
Nœuds frères : qui ont le même parent
Feuille ou nœud externe : qui n’a pas
d’enfant
Point double ou nœud interne : qui a
deux enfants
Branches, couches
7
Branche (a, b) où b descend de a
Ensemble : a, b et ancêtres de b qui
descendent de a
Branche extérieure gauche vs.
droite
Branche (racine, feuille la plus à gauche vs.
droite)
Couche de profondeur n
Ensemble des nœuds situés à la profondeur
n
Mesures
8
Distance entre deux nœuds..
.. qui forment une branche (a, b)
Nombre d’éléments de la branche moins un
Profondeur d’un nœud
Sa distance à la racine
Note : la racine est donc de profondeur 0
Hauteur d’un arbre
Profondeur de la plus profonde feuille + 1
Sous-arbres, définition récursive
9
Pour tout nœud
Le fils gauche est la racine du sous-arbre
gauche
Le fils droit est la racine du sous-arbre droit
Définition alternative (récursive) d’un
arbre
Un arbre est un triplet (a, G, D) composé
D’un nœud racine a
D’un sous-arbre gauche G
D’un sous-arbre droit D
Arbres spéciaux
10
Arbre vide
N’a aucun nœud = est sans racine
Arbre entier
Dont tous les nœuds sont soit interne, soit feuille
Arbre complet
Arbre entier
Pas de feuilles dont les profondeurs diffèrent > 1
Arbre parfait
Arbre entier
Pas de feuilles dont les profondeurs diffèrent
Définition d’un nœud
11
d’arbre
Structure nœud comportant trois
champs
1. un élément e
2. l’adresse mémoire sag du sous-arbre
gauche
3. l’adresse mémoire sad du sous-arbre
droit
sag
A sad
Assembler un arbre
12
Notez l’identité maillon LDC et nœud
d’AB
Les deux pointeurs changent de nom
(convention)
Ce qui change fondamentalement
La manière d’assembler les instances de ces
maillons
A A B
Graphe acyclique on s’interdit de former des
cycles
NON ! NON !
Assembler un arbre
13
A
B C
D
OUI !
Définition d’un nœud d’AB
14
En langage algorithmique
Algorithme 41 : Définition de la structure d’un nœud d’arbre binaire (AB)
type… : T
type structure nœud
info : T
sag, sad : adresse nœud
fin
type adresse nœud : arbre
Deux utilitaires utiles
15
Algorithme 42 : EstArbreVide (a : arbre) :
booléen
Donnée : L’arbre a à évaluer
Résultat : Un booléen
début
si a = nul alors retourner vrai
sinon retourner faux
fin
16
Algorithme 43 : CréerNoeud (e :T) : arbre
Variable locale : L’arbre a créé et initialisé (arbre
singleton)
Résultat : a
Début
a réserver nœud
ainfo e
a sag nul
a sad nul
retourner a
fin
23 Arbres
Arbre binaire (AB)
Représentation SDD d’un AB
Algorithmes de parcours d’un AB
Arbre binaire de recherche (ABR)
…
Parcourir les nœuds d’un
18
arbre
Problème
Comment parcourir un AB, i.e.
Comment visiter chaque nœud une fois et une
seule?
Deux grand types de parcours
En largeur
Niveau par niveau
Naturellement itératif
En profondeur
Branche par branche
Naturellement récursif
Parcours en largeur(ex. wikipedia)
19
2 3
4 5 6
7 8 9
123456789
Parcours en largeur (itératif)
20
Algorithme 44 : ParcoursLargeurGD (a : arbre) (itératif avec file)
Donnée : L’arbre a parcouru
Variable locale : la file auxiliaire f
début
si EstArbreVide (a) alors retourner
InitialiserFile (f)
Enfiler (f, a)
tant que non EstFileVide (f) faire
a Défiler (f)
// c’est ici que l’on fait qqch avec le nœud courant
afficher (a info)
Si non EstArbreVide (asag) alors Enfiler (f, asag)
Si non EstArbreVide (asad) alors Enfiler (f, asad)
fin
fin
Parcours en largeur
21
1 9 8 7 6 5 4 3 2 1
2 3
4 5 6
7 8 9
123456789
Parcours en largeur
22
Implémentation récursive ?
Sans lien direct entre frères, ça parait
difficile !
Pour parcourir de droite à gauche ?
Deux instructions à permuter !
Pour généraliser à un arbre n-aire ?
A chaque itération de la boucle
Défiler le nœud courant
Enfiler tous ses successeurs directs
Parcours en profondeur
23
Conception naturellement récursive
Série de trois instructions
Traitement du nœud courant (le parent)
Appels récursif sur ses deux enfants
On distingue trois types de parcours
Selon l’ordre de ces trois instructions
Pré-ordre (ou préfixe)
parent puis enfants
In-ordre (ou infixe)
premier enfant, parent, puis second enfant
Post-ordre (ou postfixe)
enfants puis parent
Parcours en profondeur pré-ordre
24
2 3
4 5 6
7 8 9
124578369
Parcours en profondeur in-ordre
25
2 3
4 5 6
7 8 9
427581396
Parcours en profondeur post-ordre
26
2 3
4 5 6
7 8 9
478529631
Parcours en profondeur (récursif)
27
Algorithme 45 : ParcoursPréordre (a : arbre)
(récursif)
Donnée : l’arbre a parcouru
début
si EstArbreVide (a) alors retourner
// c’est ici que l’on fait qqch avec le nœud
courant
afficher(a info)
ParcoursPréordre (a sag)
ParcoursPréordre (a sad)
fin
Parcours en profondeur (itératif)
28
Algorithme 46. Parcours en profondeur préordre (a : arbre) //itératif avec pile
Donnée l’arbre a parcouru
Variable locale la pile auxiliaire p
début
p créer ()
si a <> null alors
empiler (p, a)
tant que non EstPileVide (p) faire
a dépiler (p)
//c’est ici qu’on traite le nœud courant
afficher (a info)
si asad <> NULL alors empliler (p, asad)
si asag <> NULL alors empliler (p, asag)
fin
fin
fin
Parcours en profondeur
29
2 3
4
7
4 5 6
2
5
8
1
3
9
6
7 8 9
124578369
Exercices
30
Exercice 1 :
a) Concevoir un algorithme récursif pour parcourir un arbre
binaire en in-ordre
b) Concevoir un algorithme récursif pour parcourir un arbre
binaire en post-ordre
Quelques exemples d’algorithmes
31
Compter le nombre d’éléments d’un AB
Libérer la mémoire occupée par un arbre
Mesurer la hauteur d’un AB
Soient deux adresses de nœuds
Vérifier qu’ils forment branche
i.e. que le premier est l’ancêtre du second
Retourner cette branche sous forme de LSC
Créer un arbre parfait de hauteur n dont les
nœuds contiennent respectivement 1, 2, …,
2n + 1 – 1, suivant un parcours en largeur
Compter le nombre
32
d’éléments
Algorithme 47 : NombreElements (a : arbre) : entier (récursif)
Donnée : L’arbre a dont on compte les éléments
Résultat : le nombre d’éléments de a
début
si EstArbreVide (a) alors retourner 0
sinon retourner (1 + NombreElements(asag) + NombreElements (asad))
fin
Exercices
33
Exercice 2 – Analyser un AB, connaître le vocabulaire
Concevoir des algorithmes pour évaluer (compter ou mesurer) sur un
AB :
a) le nombre de feuilles,
b) le nombre de points doubles,
c) le nombre de nœuds monoparentaux.
Exercice 3 – Un peu de réflexion algorithmique ()
Concevoir un algorithme qui retourne le nombre de nœuds d’un arbre situés à une
profondeur donnée.
En vous appuyant sur l’algorithme précédent, concevoir un algorithme itératif qui retourne
le niveau de profondeur d’un arbre qui contient le plus grand nombre de nœuds.
Par convention, la profondeur de la racine est zéro.
Libérer la mémoire
34
Algorithme 48 : LibérerArbre (a : arbre)
Donnée modifiée : L’arbre a qu’on libère
début
si non EstArbreVide (a) alors
LibérerArbre (asag)
LibérerArbre (asad)
liberer (a)
fin
Mesurer la hauteur
35
Algorithme 49 : Hauteur (a : arbre) : entier
Donnée: L’arbre a que l’on mesure
Résultat : la hauteur de l’arbre
Variable locale : hg et hd, les hauteurs respectives des fils gauche
et droit
début
si EstArbreVide (a) alors retourner 0
sinon
hg Hauteur (asag)
hd Hauteur (asad)
si hg > hd alors retourner 1 + hg
sinon retourner 1 + hd
fin
fin
Vérifier la branche
36
Le problème peut être reformulé
Vérifier que le second nœud représente un
sous-arbre de l’arbre représenté par le
premier
Algorithme 50 : EstSousArbre (a, b : arbre) : booléen
Donnée: Deux arbres a et b dont on vérifie si le deuxième est sous-
arbre du premier
Résultat : Un booléen vrai ssi b est un sous-arbre de a
début
si EstArbreVide (a) ou EstArbreVide (b) alors retourner faux
si a = b alors retourner vrai
retourner EstSousArbre (asag, b) ou EstSousArbre (asad, b)
fin
Arbre Binaire de Recherche (ABR)
37
C’est un AB tel pour tout nœud a
max(sag(a)info) < ainfo ≤ min(sad(a)info) (ordre
croissant)
max(sad(a) info) < a info ≤ min(sag(a) info ) (ordre
décroissant)
La relation porte
Sur les éléments des nœuds et non pas sur les adresses
Il s’agit d’une relation d’ordre (bon ordre)
Elle en induit une par niveau
Les frères sont bien ordonnés
Si on impose <, relation d’ordre totale
Pour consultation de la littérature
EN : Binary Search Tree (BST)
ABR
38
3
0
1 5
5 0
1 3 6
0 5 0
1
1
1
Non ABR
39
3
0
1 5
5 0
1 3 6
0 5 0
1
1
6
Rechercher un élément
40
Simple dichotomie
Plus besoin d’algorithme récursif et couteux
Une boucle d’itération suffit
Illustration : on recherche
41
11
3
0 11 <<30 ??VV
G
11 15 G
11 < 10 ? VD
1 5
5 0
1 3 6
0 5 0
1
1
1
L’algorithme (version
récursive)
42
Si l’on souhaite compter le nombre
d’occurrences
Ajouter un compteur
Si ordre strict, c’est inutile
Algorithme 51: Recherche ABR(a: ABR, e: T): booléen
Donnée : L’ABR a dans lequel on effectue la recherché, l’élément recherché e
Résultat : un booléen vrai ssi e est un élément de a
début
si a = NULL alors retourner faux
si a info = e alors retourner vrai
si e < ainfo alors retourner Recherche ABR(a sag, e)
sinon retourner Recherche ABR(a sad, e)
fin
L’algorithme (version
43
itérative)
Algorithme 52: Recherche ABR(a: ABR, e: T): booléen
Donnée : L’ABR a dans lequel on effectue la recherché, élément
recherché e
Résultat : un booléen vrai ssi e est un élément de a
début
tant que non EstArbreVide (a) faire
si a info = e alors retourner vrai
si e < ainfo alors a a sag
sinon a a sad
fin
retourner faux
fin
Exercices
44
Exercice 4
Construire un ABR en y rajoutant au tour de rôle :
15, 24, 18, 10, 8, 14, 11, 13, 27
8, 27, 24, 10, 13, 11, 15, 18, 14
13, 11, 18, 27, 14, 24, 15, 8, 10
Ajouter un élément
45
Ajout au niveau des feuilles
1. Si l’arbre est vide, on créée une racine
2. Sinon on itère jusqu’au point d’insertion
Placevide sag ou sad dont on mémorise le
parent
On crée le nœud et on l’insère
C’est donc une adaptation de la
recherche
Ajouter un élément
46
Algorithme 53 : AjoutABR (a : ABR, e : T)
Donnée modifiée : L’ABR a dans lequel on effectue l’ajout
Donnée : l’élément e à rajouter
Variables locales : Une variable b pour itérer a (en donnée modifiée) et l’adresse p du parent du point d’insertion
début
si EstArbreVide (a) alors a NouveauNoeud (e)
sinon
b a
tant que non EstArbreVide (b) faire
p b
si e < binfo alors b bsag
sinon b bsad
fin
b Créernoeud (e)
si e < pinfo alors psag b
sinon psad b
fin
fin
Exercices
47
Exercice 5
Concevoir un algorithme récursif pour ajouter un élément dans un ABR.
Supprimer un élément
48
A) Pour supprimer une feuille, Rien à Signaler
B) Pour supprimer un nœud à un seul enfant
Raccorder l’enfant en lieu et place du parent supprimé
C) Sinon, c’est un peu plus compliqué, mais à peine
Soit a le nœud à supprimer et :
m = max(asaginfo) , M = min(a sadinfo)
Observons que m et M ne peuvent avoir deux enfants !!!
1. Permuter la valeur du nœud à supprimer avec m ou avec
M
La structure d’ABR est préservée !!!
2. Supprimer celui de m ou M qu’on a permuté avec a
On se ramène à l’un des deux cas A) ou B) !!!
Supprimer un élément : illustration
49
Le cas C) (source : wikipedia – copyleft)
Exercices
50
Exercice 6 – ABR : opération utilitaire élémentaire pour préparer
le 4
Concevoir un algorithme itératif pour récupérer l’adresse du nœud qui
contient le plus petit élément d’un ABR. Même question avec le plus
grand élément.
Concevoir un algorithme itératif pour échanger un élément d’un ABR
avec le plus grand élément de son sous-arbre gauche. Même question
avec le plus petit élément de son sous-arbre droit.
Problème 7 – Supprimer un élément d’un ABR
Concevoir un algorithme pour supprimer un élément dans un ABR.
Indice – traiter d’abord le cas de la feuille, puis celui du nœud
monoparental. Réfléchir ensuite à un moyen, en vous appuyant sur
l’exercice précédent, de ramener le cas du point double au cas du nœud
monoparental.
En C/C++, on appelle cette adresse un « pointeur ».
Exercices
51
Problème 8 – Vérifier qu’un AB est un ABR
Concevoir un algorithme pour reconnaître si un AB donné est un ABR ou
non.
Commencer par concevoir les algorithmes pour retourner plus petit et
plus grand éléments d’un AB.
Utiliser ces algorithmes pour effectuer la vérification de la condition
d’ordre locale entre la racine et ses deux sous-arbres (cf. définition de
l’ABR).
Vérifier que l’arbre et chacun de ses sous-arbres vérifie cette condition
cf. définition de l’ABR).
Problématique
Cas : ajouts dans un ABR
Rappel : nouvelles feuilles
Ex. Ajout de N éléments supérieurs à la racine
Tous ces éléments sont ajoutés dans le SAD
L’arbre se déséquilibre fortement à droite
Besoin d’une technique dynamique
d’équilibrage
But
Maintenir l’équilibre au fur et à mesure des ajouts
Moyen
Restructurer (si nécessaire) l’ABR après chaque ajout
52
Arbre équilibré
53
Equilibre parfait (resp. partiel)
Un AB est parfaitement (resp. Partiellement) équilibré ssi, pour
chacun de ses sous-arbres, la différence entre le nombre de nœuds
(resp. la hauteur) du SAG et du SAD est au plus 1.
Formellement
Pour tout nœud a de l’arbre
|nsad(a) – nsag(a)| (resp. |hsad(a) – hsag(a)|) ≤ 1
Attention
Comme pour le caractère d’ABR d’un AB
Vérification récursive de la propriété indispensable (err. Fréquente)
Notes
Propriété purement structurale
Elle n’est pas liée au caractère d’ABR de l’AB
Equilibre parfait Equilibre partiel
Réciproque fausse
Arbre équilibré
54
Lesquels sont partiellement (resp.
parfaitement) équilibrés ?
Algorithme de vérification
55
Algorithme 54 : EstEquilibréParfaitement (a : arbre) : booléen
Donnée : a de type arbre, l’arbre dont on vérifie l’équilibre
Résultat : un booléen qui indique si a est équilibré
début
si a = nul alors
retourner vrai
fin
si valeur absolue (NombreEléments(asag) - NombreEléments (asad)) > 1
alors
retourner faux
fin
si non EstEquilibré (asag) alors
retourner faux
fin
retourner (EstEquilibré (asad))
fin
Exercices
56
Exercice 9 – Mesure d’équilibre
Concevoir un algorithme pour vérifier qu’un arbre est partiellement
équilibré.
Modèle AVL
57
Principe
Arbre capable de maintenir un équilibre partiel
Adelson-Velskii et Landis (auteurs, 1962)
Spécification
Structure de maillon d’AB
Ajout d’un champ pour mémoriser
Le facteur d’équilibrage
= état d’équilibre
i.e. valeur locale de hsad(a) – hsag(a)
Algorithmes d’ajout / suppression
Objectif : maintenir balance, en tout nœud
dans le domaine {-1, 0, 1}
Rotation simple droite (D)
58
Arbre plus haut par la gauche (facteur d’éq. = -1)
Ajout d’une feuille dans le SAG du SAG
Rotation simple D
A devient fils droit de B
Le SAD de B devient le SAG de A
Application à l’AVL
59
Après ajout, soit a l’ascendant du nœud ajouté
Le plus proche du nœud ajouté
Et dont le facteur d’équilibrage devient ±2
4 cas à distinguer
Selon que la nouvelle feuille est ajoutée
Respectivement dans l’un des 4 sous-arbres
SAG(SAG(a)), SAD(SAG(a)), SAD(SAD(a)), SAG(SAD(a))
(En fait deux, à la symétrie verticale près)
4 cas d’ajout de la nouvelle feuille dans le sous-arbre :
GG Gauche-Gauche
GD Gauche-Droite
DD Droite-Gauche
DG Droite-Gauche
Rotation double gauche droite (GD) –
Exemple 1
60
Arbre plus haut par la gauche (facteur d’éq. = -1)
Ajout d’une feuille dans le SAD du SAG
Rotation double GD
A et B deviennent respectivement fils droit et gauche de C
Le SAG et le SAD de C deviennent respectivement les SAD
de B et SAG de A
Rotation double gauche droite (GD) –
Exemple 2
61
Arbre plus haut par la gauche (facteur d’éq. = -1)
Ajout d’une feuille dans le SAD du SAG
Rotation double GD
A et B deviennent respectivement fils droit et gauche de C
Le SAG et le SAD de C deviennent respectivement les SAD
de B et SAG de A
Exemple 1 (animé)
62
8
9 Rotation
Ajout de 5
9
2
1
G
D
10
GD
1
8
2 9
0
1
2
1 8
5 0
1 5
Exercices
63
Exercice 10 – Ajout dans un AVL : exécution d’un cas
Exécuter l’algorithme d’ajout d’éléments dans un AVL pour les éléments
6, 4, 7, 1, 5, 3, 8, 9, 10, 2, pris dans cet ordre.
Exercice 11 – Ajout dans un AVL : conception de l’algorithme
Concevoir un algorithme récursif d’ajout d’un élément dans un AVL
comme fils gauche ou fils droit d’un nœud feuille ou simple donné.
Vérifier que l’opération est licite, i.e. que le nœud donné est bien un
nœud de l’arbre, et que l’ajout est possible, c'est-à-dire que la place de
l’ajout n’est pas déjà occupée.
Si l’opération est licite, ajouter effectivement l’élément, et recalculer la
« balance » de chacun des nœuds de l’AVL.
Modifier l’algorithme précédent en effectuant l’opération de
rééquilibrage nécessaire au moment où un déséquilibre est constaté,
avant de propager plus avant vers le sommet la mise à jour de la
balance de chacun des nœuds.
EXERCICES SUPPLÉMENTAIRES POUR LES PLUS COURAGEUX
64
Exercice 12 – Manipulations structurales simples sur un AB
Concevoir un algorithme qui transpose toutes les nœuds feuilles d'un
arbre avec leur frère.
Concevoir un algorithme pour transformer un AB en son symétrique par
rapport à l'axe vertical (pour chaque niveau de profondeur, l’ordre des
nœuds est inversé entre la droite et la gauche).
Exercice 13 – Création des listes
Concevoir des algorithmes pour retourner sous forme de liste
simplement chaînée selon un parcours en profondeur main gauche ou un
parcours en largeur de gauche à droite :
la liste des éléments,
la liste de feuilles,
la liste des points doubles,
la liste des nœuds monoparentaux.
d’un AB.
EXERCICES SUPPLÉMENTAIRES POUR LES PLUS COURAGEUX
65
Exercice 14 – Extractions de listes moins triviales
Concevoir un algorithme pour extraire sous forme de LSC, à partir d’un
AB :
Les branches extérieures gauche et droite,
Une coupe transversale de profondeur donnée (de gauche à droite),
Une branche située entre la racine et un nœud quelconque.
Note – vous pouvez concevoir les LSC à extraire comme des LSC
contenant les adresses des nœuds, ou bien celles des éléments, ou bien
encore, une copie des éléments de l’arbre.
Problème 15 – Adressage logique dans un AB
L’adresse logique relative d’un sous-arbre d’un arbre, exprimée en
binaire, est constituée d’autant de bits que de nœuds sur la branche qui
relie la racine de l’arbre au sous-arbre. Le premier bit est
nécessairement 1 (adresse de la racine), les suivants sont 0 ou 1
respectivement pour indiquer un prochain pas vers la gauche ou vers la
droite : par exemple 101 est l’adresse du fils droit du fils gauche de la
racine.
EXERCICES SUPPLÉMENTAIRES POUR LES PLUS COURAGEUX
66
Exercice 16 – Retrait dans un AVL : exécution d’un cas
Exécuter l’algorithme de retrait d’éléments dans l’AVL obtenu à l’issue
de 8.1 pour les éléments 6, 4, 7, 8, 9, 10, 2, 1, 5, 3, pris dans cet ordre.
Exercice 17 – Equilibrer un ABR
Etant donné un ABR quelconque, et en vous inspirant des algorithmes
précédents, concevoir un algorithme pour :
Transformer cet ABR en AVL (s’il ne l’est pas déjà).
Transformer cet ABR en arbre parfaitement équilibré.