Explorer les Livres électroniques
Catégories
Explorer les Livres audio
Catégories
Explorer les Magazines
Catégories
Explorer les Documents
Catégories
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.
Terminologie :
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.
* niveau de la racine = 1
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 graphique :
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
Un autre objet de données pouvant être utilisé pour représenter un arbre : l’arbre binaire
Définition:
Un arbre binaire est caractérisé par le fait que tout nœud a au plus 2 fils.
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.
+ -
a / d *
b c e f
Lemme :
(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
* Un Arbre binaire étendu est un arbre binaire où chaque nœud possède 0 ou 2 fils.
*Un Arbre binaire complet est un arbre binaire étendu où toutes les feuilles sont au
même niveau.
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 en Préfixé:
ou
Debut
Si A ≠ NIL
Prefixe ( A^.fg )
Prefixe ( A^.fd )
Fsi
Fin
Parcours en Infixé:
ou
Infixe( A : AB )
Debut
Si A ≠ NIL
Traiter ( A^.val )
Infixe ( A^.fd )
Fsi
Fin
Parcours en Postfixé:
ou
Debut
Si A ≠ NIL
Postfixe ( A^.fd )
Traiter ( A^.val )
Fsi
Fin
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
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 :
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.
ABR = ^nœud
nœud = enreg
val : valeur
fg : ABR
fd : ABR
fenreg
Version Itérative :
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.