Vous êtes sur la page 1sur 13

LES ARBRES

Introduction :

La récursivité peut être utilisée de façon élégante et efficace dans la définition de structures
de données beaucoup plus sophistiquées que les listes et les séquences.

Les arbres sont un type de données récursif. On les trouve pratiquement dans tous les
problèmes où il existe une solution utilisant le principe de la dichotomie. Ils offrent une
grande souplesse pour représenter des ensembles sur lesquels on veut pratiquer les
opérations les plus diverses : insertion, extraction, réunion,….Ils permettent également de
représenter des objets structurés tels que expressions arithmétiques, structure d’une
phrase…..

Définition :

L’arbre est un cas particulier du graphe où tout sommet possède 0 ou plusieurs successeurs
et il existe un seul sommet avec 0 prédécesseur les autres sommets possèdent chacun 1
seul prédécesseur.

La structure d’arbre de type de base T est définie par :

- La structure vide ( ^ ) est un arbre


- Un sommet de type T avec un nombre fini de successeurs (appelés sous arbres)
est un arbre.

Terminologie :

Dans un arbre le sommet est appelé Nœud.

Il existe une unique relation : la relation PÈRE – FILS.

On appelle Père le prédécesseur d’un nœud et fils son successeur.

On appelle Racine de l’arbre l’unique nœud sans père.

Une Feuille est nœud sans fils. Dans un arbre il existe au moins une feuille.

Tout nœud avec un père et au moins un fils est appelé Nœud interne.

Le Degré d’un nœud est le nombre de ses fils. De ce fait, le Degré d’un arbre est égale au
plus grand degré de ses nœuds.

Le Niveau d’un nœud est déterminé de la manière suivante :

* niveau de la racine = 1

* si le niveau d’un nœud est i ses fils ont le niveau (i+1)


La Hauteur d’un arbre est égale au plus grand niveau de ses nœuds.

Une forêt est un ensemble de n arbres disjoints (n ≥ 0). Si on supprime la racine d’un arbre
on obtient une forêt.

Représentation d’un arbre :

Représentation graphique :

il existe plusieurs façons de représenter graphiquement un arbre. La plus intuitive est


inspirée de la représentation d’un graphe.

Exemple : Soit T = { A, B, C, D, E, F, G}, une structure d’arbre de type de base T peut être
représentée par la figure suivante :

B C E

D F G

La racine de cet arbre est le nœud A. Cet arbre possède 4 feuilles (B, D, F et G) et 2 nœuds
internes (C et E). Il est de degré 3 et de hauteur 3.

Représentation en mémoire :

En optant pour une structure dynamique, l’arbre peut être représenté par une liste chainée
de la manière suivante :

Arbre = ^nœud

noeud = enreg

val : valeur

lv : Arbre

lh : Arbre

fenreg

où le champ lv donne accès au premiers fils (le plus à gauche) d’un nœud, alors que le
champs lh est utilisé pour accéder aux autres fils du nœud.
Exemple : la représentation de l’arbre de l’exemple précédent est la liste chainée suivante :

A Nil

B Nil C E Nil

D Nil Nil F Nil G Nil Nil

Un autre objet de données pouvant être utilisé pour représenter un arbre : l’arbre binaire

Les arbres binaires :

Définition:

Un arbre binaire est caractérisé par le fait que tout nœud a au plus 2 fils.

Un arbre binaire est défini récursivement par:

* l’arbre vide ( ^ ) est un arbre binaire

* l’arbre A = < A1 , x , A2 > est un arbre binaire si et seulement si A1 et A2 sont des


arbres binaires.

Dans un arbre binaire on distingue un sous arbre gauche appelé fils gauche et un sous arbre
droit appelé fils droit.

Exemple : une expression arithmétique avec des opérations binaires peut être représentée
par un arbre binaire.

L’expression (a+(b/c))*(d-(e*f)) est représentée par l’arbre :

+ -

a / d *

b c e f
Lemme :

(i) Le nombre maximum de nœuds au niveau i d’un arbre binaire est :


2i-1 ( i ≥ 1)

(ii) Le nombre maximum de nœuds dans un arbre binaire de hauteur h est :


2h - 1 ( h ≥ 1)

(iii) Pour un arbre binaire non vide, si n1 est le nombre de feuilles et n2 est le nombre
de nœuds de degré 2, alors :
n1 = n2 + 1

Arbres binaires particuliers:

* Un Arbre binaire étendu est un arbre binaire où chaque nœud possède 0 ou 2 fils.

Un arbre binaire étendu de hauteur h a 2h - 1 nœuds (c’est un arbre binaire maximum de


hauteur h).

*Un Arbre binaire complet est un arbre binaire étendu où toutes les feuilles sont au
même niveau.

Représentation d’un arbre binaire:

1) Représentation statique :
Une représentation séquentielle élégante d’un arbre binaire étendu est déterminée
en numérotant séquentiellement les nœuds, en commençant par la racine, puis les
nœuds du niveau 2, puis les nœuds du niveau 3, …..
Les nœuds d’un même niveau sont numérotés de gauche à droite.
Exemple : 1
2 3

4 5 6 7

8 9
Les nœuds d’un tel arbre peuvent être stockés dans un tableau vecteur tel que le
nœud numéro i est stocké à la position i.
C’est une représentation idéale pour les arbres binaires étendus puisqu’il n’y a pas de
perte de place.
Inconvénient : Cette représentation souffre de certaines inadéquations des
représentations séquentielles. En effet, l’insertion ou la suppression de nœuds du
milieu d’un arbre exige le parcours de plusieurs nœuds de l’arbre pour changer leurs
numéros.
Solution : représentation dynamique
2) Représentation dynamique :
Un arbre est représenté par un ensemble de nœuds. Chaque nœud est représenté
par une structure à 3 champs.
Comme tous les nœuds d’un arbre sont accessibles à partir de sa racine, un arbre est
en fait représenté par un pointeur vers celle-ci.

AB = ^nœud
noeud = enreg
val : valeur
fg : AB
fd : AB
fenreg

Parcours d’arbre binaire :

Outre les opérations usuelles comme la recherche, l’insertion et la suppression, on définit


une opération très particulière qu’est le parcours d’un arbre binaire. Parcourir un arbre
binaire c’est visiter chaque nœud de l’arbre une et une seule fois. Cette opération est
utilisée quand on veut effectuer un certain traitement sur tous les nœuds de l’arbre sans
répétition (par exemple afficher les valeurs d’un arbre).

On peut visiter les nœuds d’un arbre suivant divers ordres.

Supposons que les opérations G, T, D désignent déplacement Gauche, Traitement nœud,


déplacement Droit. Pour un nœud il y a alors 6 combinaisons possibles pour déterminer
l’ordre de priorité des tâches : GTD ; GDT ; TGR ; TDG ; DTG ; DGT.

Si on exige que le parcours gauche soit avant le parcours droit, on a alors 3


combinaisons :GTD ; GDT ; TGD

Pour ces dernières on donne les noms : infixé , postfixé et préfixé.

Nous allons définir 3 méthodes de parcours selon la priorité donnée au traitement :

Parcours en Préfixé:

Père – fils gauche – fils droit

ou

Père – fils droit – fils gauche


Prefixe( A : AB )

Debut

Si A ≠ NIL

Alors Traiter ( A^.val )

Prefixe ( A^.fg )

Prefixe ( A^.fd )

Fsi

Fin

Parcours en Infixé:

fils gauche –Père – fils droit

ou

fils droit – Père – fils gauche

Infixe( A : AB )

Debut

Si A ≠ NIL

Alors Infixe ( A^.fg )

Traiter ( A^.val )

Infixe ( A^.fd )

Fsi

Fin

Parcours en Postfixé:

fils gauche – fils droit –Père

ou

fils droit– fils gauche – Père


Postfixe( A : AB )

Debut

Si A ≠ NIL

Alors Postfixe ( A^.fg )

Postfixe ( A^.fd )

Traiter ( A^.val )

Fsi

Fin

Exemple : Si A est l’arbre : a

b c

d e
Les nœuds sont traités dans l’ordre suivant : préfixé : a b d c e

Infixé : b d a c e

Postfixé : d b e c a

Applications sur les Arbres Binaires :

1) Pour trier une suite d’éléments on la représente par un arbre binaire ordonné et on
utilise le parcours de l’arbre pour lister les valeurs ( exemple arbre binaire de
recherche et parcours infixé).
2) Pour évaluer une expression arithmétique on la représente par un arbre binaire puis
on utilise le parcours en postfixé.

Remarque : L’arbre binaire représenté par une liste chainée présente un inconvénient
majeur quand il est utilisé dans un contexte où l’opération de recherche est très fréquente
puisqu’elle ne peut être que séquentielle. La structure de tableau trié permet un accès très
rapide à chaque élément grâce à la recherche dichotomique. Par contre les insertions et
extractions sont relativement malaisées puisqu’elles obligent à déplacer en moyenne la
moitié des éléments. La structure d’arbre introduit des changements nettement plus
substantiels et permet d’écrire des algorithmes de recherche, d’insertion et d’extraction
tous de complexité peu élevée grâce au principe d’arbre ordonné et en l’occurrence l’arbre
binaire de recherche.
L’Arbre Binaire de Recherche :

Définition :

L’arbre binaire de recherche (A.B.R) est un arbre ordonné. La propriété d’être un A.B.R peut
être définie récursivement de la manière suivante :

* l’arbre vide ^ est un ABR

* A = < A1 , x , A2 > est un ABR ssi

- A1 et A2 sont des ABR

- A1 ≤ x < A2 autrement dit, toute valeur dans A1 est ≤ x et toute valeur


dans A2 est > x.

Exemple : 6

4 7

2 5

Remarque : Le parcours en infixé (gauche- père – droit) d’un A.B.R fournit une liste triée en
ordre croissant.

Représentation d’un arbre binaire de recherche :

ABR = ^nœud
nœud = enreg
val : valeur
fg : ABR
fd : ABR
fenreg

Opérations sur les A.B.R :

1) Recherche dans un A.B.R :


Soit A un A.B.R et x une valeur, le résultat de la recherche de x dans A est un pointeur
ptx sur x, si x figure dans l’arbre, sinon NIL.
Version récursive :

Recherche_ABR( A : ABR ; x : valeur ; var ptx : ABR)


Debut
Si A = NIL alors ptx := NIL
sinon si A^.val = x alors ptx := A
sinon si x < A^.val alors Recherche_ABR( A^.fg , x , ptx )
sinon Recherche_ABR( A^.fd , x , ptx )
fsi
fsi
fsi
Fin

Version Itérative :

Recherche_ABR( A : ABR ; x : valeur ; var ptx : ABR )


var trouve : booléen
Debut
ptx := A
trouve := faux
tant que ptx ≠ NIL et NON trouve faire
si ptx^.val = x alors trouve := vrai
sinon si x < ptx^.val alors ptx := ptx^.fg
sinon ptx := ptx^.fd
fsi
fsi
fait
Fin

2) Insertion dans un A.B.R :


Soit A un A.B.R et x une valeur à insérer dans A. Il n’existe qu’une seule position pour
ajouter x dans A. x sera ajouté en tant que feuille, à gauche d’un nœud qui ne
possède pas de fils gauche si x est ≤ à la valeur de ce nœud, ou à droite d’un nœud
qui ne possède pas de fils droit si x est > à la valeur de ce nœud.
Version récursive :
Insere_ABR( var A : ABR ; x : valeur)
Debut
Si A = NIL alors genere(A,x)
sinon si x ≤ A^.val alors Insere_ABR( A^.fg , x )
sinon Insere_ABR( A^.fd , x )
fsi
fsi
Fin
genere(var A:ABR; x : valeur)
Debut
créer( A )
A^.val := x
A^.fg := NIL
A^.fd := NIL
Fin
Version Itérative :
Insere_ABR( var A : ABR ; x : valeur)
var p,q : ABR
Debut
créer( p )
p^.val := x
p^.fg := NIL
p^.fd := NIL
si A = NIL alors A := p
sinon q := A
arret := faux
répéter
si x ≤ q^.val alors si q^.fg = NIL
alors q^.fg := p
arret := vrai
sinon q := q^.fg
fsi

sinon si q^.fd = NIL


alors q^.fd := p
arret := vrai
sinon q := q^.fd
fsi
fsi
jqa arret
fsi
Fin

3) Suppression dans un A.B.R :


Soit A un A.B.R et x une valeur. On commence par localiser x dans A, 2 situations
possibles :
- x ne se trouve pas dans A, l’arbre est alors inchangé.
- x figure dans A, 3 cas possibles :
• x est une feuille, on supprime la feuille
• x a un seul fils, on remplace le nœud de valeur x par son unique fils
• x a 2 fils, on remplace la valeur x par la plus grande valeur sous son fils
gauche ou bien la plus petite valeur sous son fils droit et on supprime le
nœud qui a donné cette valeur.

Version récursive :
Supprime_ABR ( var A:ABR ; x : valeur)
Debut
si A ≠ NIL alors si A^.val = x
alors delete_noeud(A)
sinon si x < A^.val
alors Supprime_ABR(A^.fg , x)
sinon Supprime_ABR(A^.fd , x)
fsi
fsi
fsi
Fin
Delete_noeud (var A:ABR)
Debut
p := A
si A^.fg = NIL alors A := A^.fd
liberer( p )
sinon si A^.fd = NIL alors A := A^.fg
liberer( p )
sinon upd_noeud(A , A^.fg)
fsi
fsi
Fin
upd_noeud ( var A , ptrfils : ABR )
Debut
si ptrfils ^.fd ≠ NIL
alors upd_noeud(A , ptrfils^.fd)
sinon A^.val := ptrfils ^.val
p := ptrfils
ptrfils := ptrfils ^.fg
liberer( p )
fsi
Fin
Version Itérative :
supprime_ABR( var A : ABR ; x : valeur)
var p, père, q : ABR
Debut

p := A
père := NIL
trouve := faux
tant que p ≠ NIL et NON trouve faire
si p^.val = x alors trouve := vrai
sinon père := p
si x < p^.val alors p := p^.fg
sinon p := p^.fd
fsi
fsi
fait
si trouve alors si p^.fg = NIL et p^.fd = NIL
alors si père = NIL
alors liberer( p )
p := NIL
sinon si père^.fg = p
alors père^.fg := NIL
sinon père^.fd := NIL
fsi
liberer( p )
Sinon si p^.fg = NIL alors si père = NIL
alors q := p
p := p^.fd
liberer(q)
sinon si père^.fg = p
alors père^.fg := p^.fd
sinon père^.fd := p^.fd
fsi
Liberer( p )
fsi
Sinon si p^.fd = NIL alors si père = NIL
alors q := p
p := p^.fdg
liberer(q)
sinon si père^.fg = p
alors père^.fg := p^.fg
sinon père^.fd := p^.fg
fsi
Liberer( p )
Fsi
Sinon supprime_noeud( p , p^.fg)
Fsi
Fsi
Fsi
Fsi
supprime_noeud( var p : ABR ; G : ABR )
var q , r, père : ABR
Debut
q := G
père := NIL
tant que q^.fd ≠ NIL faire
père := q
q := q^.fd
fait
p^.val := q^.val
r := q
si père = NIL
alors p^.fg := q^.fg
sinon père^.fd := q^.fg
Liberer( q )
Fsi
Fin
Conclusion :
Nous constatons que pour les 3 opérations, la complexité est au plus égale à la hauteur de
l’arbre. Donc l’optimalité se mesure par la hauteur de l’arbre. Plus l’arbre est haut plus le
temps mis pour chercher, insérer ou supprimer un élément est grand.
Ainsi, dans des situations d’infortune, arbre dégénéré obtenu à partir d’insertions
successives d’éléments ordonnés, la hauteur de l’arbre est proche de sa taille. La complexité
est alors celle d’une recherche séquentielle O(n).
Plusieurs techniques permettent de maintenir l’arbre pas trop haut donc relativement
équilibré, c'est-à-dire hauteur(A) = log(taille(A)).
Ce sont les arbres de recherche équilibrés.