Vous êtes sur la page 1sur 17

Université Sétif 1 - Faculté des Sciences

Département d’informatique
Filière : Licence Académique
Module : Algorithmique et structure de données
Année universitaire : 2015-2016

CHAP 3 : STRUCTURES HIÉRARCHIQUES : ARBRES

Introduction :
La structure d'arbre est très utilisée en informatique. d’une part parce que les informations sont
souvent hiérarchisées, et peuvent être représentées naturellement sous une forme arborescente, et
d’autre part, parce que les structures de données arborescentes permettent de stocker des données
volumineuses de façon que leur accès soit efficace.

Sur le fond on peut considérer un arbre comme une généralisation d'une liste car les listes peuvent
être représentées par des arbres. La complexité des algorithmes d'insertion de suppression ou de
recherche est généralement plus faible que dans le cas des listes.

Définition :
Un arbre est une structure de données (souvent dynamique) représentant un ensemble de valeurs
organisées hiérarchiquement. Chaque valeur est stockée dans un nœud. Les nœuds sont connectés
entre eux par des relations parent/fils.
A part le nœud racine, tous les autres nœuds ont exactement un seul nœud parent et zéro ou
plusieurs nœuds fils.
Le nœud racine n'a pas de parent et possède zéro ou plusieurs fils.
Les nœuds qui n'ont pas de fils sont appelés feuilles (ou nœuds externes), les autres (ceux qui ont au
moins un fils) sont appelés nœuds internes.

Organisation hiérarchique des informations


– intuitive
– conforme à beaucoup de situations :
� arbre généalogique
� tournois sportifs
� structures syntaxiques
� relations d’inclusions entre ensembles : classification des êtres vivants
� décomposition de structures : type structuré, expression arithmétique, langue naturelle

Quelques exemples de données arborescentes


Expressions arithmétiques. On peut repré senter les expressions arithmétiques par des arbres é tiqueté s par
des operateurs, des constantes et des variables. La structure de l’ arbre rend compte de la priorité des
operateurs et rend inutile tout parenthé sage.

1
(y/2−t)×(75+z)

Exercice : Dessinez l’ arbre correspondant `a l’ expression 3(2x−1) + 1.


Arbres syntaxiques. Un arbre syntaxique repr´esente l’ analyse d’ une
phrase `a partir d’ un ensemble de r`egles qui constitue la grammaire : une
phrase est compos´ee d’ un groupe nominal suivi d’ un groupe verbal, un groupe
nominal peut-ˆ etre constitu´e d’ un article et d’ un nom commun,. . .

Arbres généalogiques. Un arbre généalogique (descendant dans le cas pré sent) repré sente la descendance
d’ une personne ou d’ un couple. Les nœuds de l’ arbre sont ´é tiqueté s par les membres de la famille et
leurs
conjoints. L’ arborescence est construite `a partir des liens de parent´e (les
enfants du couple).

2
Arbre lexicographique. Un arbre lexicographique, ou arbre en parties communes, ou dictionnaire, repré sente
un ensemble de mots. Les pré fixes communs `a plusieurs mots apparaissent une seule fois dans l’ arbre, ce
qui se traduit par un gain d’ espace mé moire. De plus la recherche d’ un mot est assez efficace, puisqu’ il
suffit de parcourir une branche de l’ arbre en partant de la racine, en cherchant a chaque niveau parmi les
fils du noeud courant la lettre du mot de rang correspondant.

Exercice : Rajoutez le mot mal.

- Définition récursive d'un arbre


La définition que nous donnons d’un arbre est récursive.
Cas particulier:
NULL est un arbre (l'arbre vide, contenant zéro nœud)
Cas général: si T est un nœud et si T1, T2, ...Tm sont des arbres, alors on peut construire un
nouvel arbre en connectant T1, T2, ...Tm comme des fils à T. chaque Ti est définit de la même
manière (récursivement). T1, T2, ... Tm sont alors des sous-arbres de T.
Plusieurs définitions sont à retenir ; nous illustrons les définitions avec l’exemple ci-dessus en
identifiant un nœud avec le caractère qu’il contient:

Etiquette : Un arbre dont tous les nœuds sont nommés est dit étiqueté. L'étiquette (ou nom du
sommet) représente la "valeur" du nœud ou bien l'information associée au nœud.
Un arbre étiqueté par A,B,…, N

3
Les fils d’un nœud sont les racines de ses sous-arbres; sur l’exemple, les fils de A sont B, C, D et E;

Le père d’un nœud x autre que la racine est l’unique nœud dont x est un fils;
Sur l’exemple, C est le père de G et H ; la racine d’un arbre n’a pas de père ;
Un nœud interne est un nœud qui a au moins un fils; sur l’exemple, D est un nœud interne;

Figure 1
une feuille d’un arbre est un nœud sans fils; sur l’exemple, F, L, H, I, J , M et N sont des feuilles;
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 ancêtres de a différents de a sont les ancêtres propres de a; sur l’exemple, les
ancêtres de K sont K, E et A ;

les descendants d’un nœud a sont les nœuds qui appartiennent au sous-arbre de racine a ; les
descendants de a différents de a sont les descendants propres de a ; sur l’exemple, les descendants
de E sont E, J, K , M et N.

Les frères d'un nœud A sont les nœuds qui possèdent le même père que A ; sur l’exemple, G et H
sont frères.

Degré d'un nœud :


Par définition le degré d'un noeud est égal au nombre de ses descendants (enfants/fils). Soient les
deux exemples ci-dessous extraits de l'arbre précédent :

Le nœud 1 est de degré 4, car il a 4 enfants/fils


Le nœud 5 n'ayant qu'un enfant son degré est 1.
Le nœud 8 est de degré 2 car il a 2 enfants.

Remarquons que lorsqu'un arbre a tous ses nœuds de degré 1, on le nomme arbre dégénéré et que
c'est en fait une liste.
Hauteur, profondeur ou niveau d'un noeud
Nous conviendrons de définir la hauteur(ou profondeur ou niveau ) d'un noeud X comme égale
au nombre de noeuds à partir de la racine pour aller jusqu'au noeud X.

4
Figure 2
En notant h la fonction hauteur d'un nœud :
Pour atteindre le nœud étiqueté 9 , il faut parcourir le lien 1--5, puis 5--8, puis enfin 8--9 soient 4
nœuds donc 9 est de profondeur ou de hauteur égale à 4, soit h(9) = 4.
Pour atteindre le nœud étiqueté 7 , il faut parcourir le lien 1--4, et enfin 4--7, donc 7 est de
profondeur ou de hauteur égale à 3, soit h(7) = 3.

L'arbre vide à une hauteur 0, et


L’arbre réduit à une racine a une hauteur 1.

Si un nœud est à une profondeur p, tous ses successeurs sont à une profondeur p+1.

Tous les nœuds d'un arbre de même profondeur sont au même niveau.
En reprenant l'arbre de la figure 1 : h(M) = 4 et h(G) = 3.

Chemin d'un noeud


On appelle chemin du noeud X la suite des noeuds par lesquels il faut passer pour aller de la racine
vers le nœud X :

Chemin du nœud 10 = (1,5,8,10)


Chemin du nœud 9 = (1,5,8,9)
.....
Chemin du nœud 7 = (1,4,7)
Chemin du nœud 5 = (1,5)
Chemin du nœud 1 = (1)

Remarquons que la hauteur h d'un nœud X est égale au nombre de nœuds dans le chemin :

h( X ) = NbrNoeud( Chemin( X ) ).

Hauteur ou profondeur d'un arbre

5
Par définition c'est le nombre de nœuds du chemin le plus long dans l'arbre. La hauteur h d'un
arbre correspond donc au nombre de niveau maximum :

h (Arbre) = max { h ( X ) /  X, X noeud de Arbre }


si Arbre =  alors h( Arbre ) = 0

La hauteur de l'arbre ci-dessous :

Un arbre peut avoir plusieurs représentations :

1. Graphe : c’est la représentation la plus utilisée comme déjà vue.

2. Indentation :

3. Ensemble imbriqué

4. Listes parenthesées :

6
Prefixé: A ( B( F ) , C ( G( L ) , H) , D( I ) , E( J, K( M, N ) ) )

Postfixé : ( ( F )B , ( ( L )G , H )C , ( I )D , ( J , ( M , N )K )E )A

 Une forêt est un ensemble d'arbres.

Implémentation :
Quand le nombre de fils de chaque élément est variable, on peut soit prévoir un tableau statique des
adresses des fils, soit prévoir un tableau dynamique, ce qui optimise l'occupation mémoire mais
complique l'ajout de fils supplémentaires. Pour avoir une gestion parfaitement dynamique, on peut
créer une liste des adresses des fils :

En fait, plutôt que de créer cette liste hors des nœuds, le plus simple (et qui utilise autant de
mémoire) est d'associer à chaque nœud l'adresse de son fils aîné, et de son frère cadet. Accéder à
tous les fils revient donc à accéder au fils aîné puis à tous ses frères:

Voici la déclaration d'un arbre général par chainage:

Code : C

Arbres binaires
Un arbre binaire est un arbre où chaque nœud est connecté à deux sous-arbres (un sous-arbre
gauche (SAG) et un sous-arbre droit (SAD) ). Ainsi le premier fils d'un nœud A est appelé fils-
gauche (SAG) et le deuxième fils est appelé fils-droit (SAD).

7
Un arbre binaire est dit « strictement binaire » si chaque nœud interne a exactement 2 fils
différents de NULL.

Dans un arbre strictement binaire, le nombre de feuilles est toujours égal au nombre de nœuds
internes + 1. Dans l'exemple précédent, il y a 4 feuilles (E, A, L et H) et 3 nœuds internes (D, F et
J).

Un arbre binaire est dit « complet » (ou complètement équilibré), s'il est strictement binaire et si
toutes les feuilles se trouvent au même niveau :

Dans un arbre binaire complet de profondeur d :


Le nombre total de nœuds = 20 + 21 + 22 + ... 2d-1 = 2d -1
Le nombre de feuilles = 2d-1

Ainsi on peut établir une équation entre la profondeur (d) d'un arbre binaire complet et le nombre
total de nœuds (n) : d = log2(n+1)

Un arbre binaire est dit parfait si, en appelant h la hauteur de l’arbre, les niveaux de profondeur 0,
1, …, h – 1 sont complètement remplis alors que le niveau de profondeur h est rempli en partant de
la gauche ; la structure d’un arbre binaire parfait est complètement déterminée par son nombre de
nœuds ; nous donnons ci-après les arbres binaires parfaits à 5 nœuds et à 12 nœuds. On rencontre
aussi quelque fois le qualificatif de presque-complet pour un tel arbre.

8
Un arbre binaire est dit équilibré si, pour tout nœud de l’arbre, les sous-arbres gauche et droit ont
des hauteurs qui diffèrent au plus de 1. L’arbre ci-dessous est équilibré.

Un arbre binaire parfait est équilibré.

Opérations de base

a. Créer_Arbre : ( ) → Arbre // crée un arbre vide


b. ArbreVide : Arbre → Booléen // renvoie vrai si l’arbre est vide, faux sinon
c. Racine : Arbre → Element // accède à l’information racine de l’arbre
d. SAG : Arbre → Arbre // accède au sous arbre gauche de l’arbre
e. SAD: Arbre → Arbre // accède au sous arbre droit de l’arbre
f. Construire Element x Arbre x Arbre → Arbre // construction d’un arbre binaire ayant
comme racine Element et 2 sous arbres données
Axiomes

Pré-conditions
a. Racine( ar ) ==> non ArbreVide( ar )
b. SAG( Ar ) ==> non ArbreVide( ar )
c. SAG( Ar ) ==> non ArbreVide( ar )

Post-conditions
a. ArbreVide(Créer_Arbre( ar )) = true
b. ArbreVide(Construire (e, ar1, ar2 )) = false
c. racine(Construire (e, ar1, ar2 )) = e
d. SAG(Construire (e, ar1, ar2 )) = ar1
e. SAD(Construire (e, ar1, ar2 )) = ar2
9
f. Construire(Racine(Ar), SAG(Ar),SAD(Ar)) = Ar

Fin des axiomes

Implantation par chainage :


Pour représenter les arbres de façon chaînée nous utilisons une structure Nœud qui contient 3
champs : un champ pour représenter l’information liée au nœud, et deux champs pointeurs vers les
sous-arbres droit et gauche.

a. Créer_Arbre : ( ) → Arbre // par déclaration


Arbre Ar = NULL ;
b. ArbreVide : Arbre → Booléen
int ArbreVide(Arbre Ar ){
return( !Ar ) ; }

c. Racine : Arbre → Element


Element Racine(Arbre Ar ){
assert(Ar != NULL, 5) ;
return( Ar->Val ) ; }
d. SAG : Arbre → Arbre
Arbre SAG(Arbre Ar ){
assert(Ar != NULL, 5) ;
return( Ar->SAG ) ; }
e. SAD: Arbre → Arbre
Arbre SAD( Arbre Ar ){
assert( Ar!=NULL , 5) ;
return( Ar->SAD ) ; }
f. Construire Element x Arbre x Arbre → Arbre
Arbre Construire( Element e , Arbre Ar1 , Arbre Ar2){
Arbre a = (Arbre)malloc(sizeof(nœud));
a->Val = e ;
a->SAG = Ar1 ;
a->SAD = Ar2 ;
return( a ) ; }

Parcours des arbres


On appelle parcours d’un arbre Ar le travail qui consiste à effectuer un certain traitement,
dépendant de l’application particulière considérée, sur l’information portée par chaque nœud de Ar.

C’est sans doute le meilleur exemple d’algorithme récursif que l’on puisse trouver. Partant du
problème « parcourir l’arbre Ar = ( i , ( Ar1 , … Arn ) ) », on obtient :

• un problème simple : « traiter i »

• n problèmes analogues au problème initial : « parcourir Ar1 », … « parcourir Arn »


10
 parcours préfixé (pré-ordre): (Racine, Gauche, Droite)

Un point reste à régler, qui dépend de l’application particulière considérée : dans quel ordre doit-on
faire les (n + 1) opérations mentionnées ci-dessus ?

Dans le cas des arbres binaires les trois manières possibles d’arranger ce parcours sont fréquemment
utilisées et ont fait l’objet de dénominations spécifiques : le parcours en pré-ordre, le parcours en
in-ordre et le parcours en post-ordre, définis respectivement par le fait que chaque nœud est traité
avant, entre ou après les parcours de ses deux fils.

 parcours préfixé (pré-ordre): ( Racine, Gauche, Droite )

Dans ce mode de parcours, le nœud courant est traité avant le traitement des nœuds gauche et droit.

Parcours en profondeur - récursif Parcours en profondeur - itératif avec pile


procedure AffichagePrefixe(a) procédure AffichagePrefixe(a)
si a n’est vide alors soit p une pile d’arbres
(* traitement de la racine *) empiler a dans p
afficher la valeur de la racine tant que p n’est pas vide faire
(* traitement des fils gauche et droit *) (* on traite l’arbre au sommet de la pile *)
AffichagePrefixe(fils gauche de a) s sommet de p
AffichagePrefixe(fils droit de a) dépiler p
fin si si s n’est pas vide alors
fin procedure afficher la valeur de la racine de s
empiler le fils droit de s dans p
empiler le fils gauche de s dans p
fin si
fin tant que
Comment dérécursiver le parcours ? fin procédure

Il ne faut pas se tromper et bien empiler le sous-


arbre droit avant le sous-arbre gauche car la pile
est une structure LIFO.

 parcours infixé (symétrique): ( Gauche, Racine, Droite )

Dans ce mode de parcours, le nœud courant est traité entre le traitement des nœuds gauche et droit.

11
 parcours postfixe ou suffixé (post-ordre): ( Gauche, Droite, Racine )

Dans ce mode de parcours, le nœud courant est traité après le traitement des nœuds gauche et droit.

Soit pour l’exemple :

• Rendu du parcours préfixe :


1, 2, 4, 5, 7, 8, 3, 6, 9
• Rendu du parcours infixe :
4, 2, 7, 5, 8, 1, 3, 9, 6
• Rendu du parcours postfixe :
4, 7, 8, 5, 2, 9, 6, 3, 1

Exercices :
1. une fonction qui calcule le nombre de nœuds dans un arbre binaire.
2. une fonction qui fait la somme des valeurs contenues dans un arbre.
3. une fonction qui calcule le nombre de feuilles dans un arbre binaire.
4. une fonction qui calcule la hauteur d’un arbre.
5. tester si un arbre est équilibré

Application :
Représentation des expressions arithmétiques
12
Les expressions arithmétiques peuvent êtres représentées sous forme d'arbre binaire. Les nœuds
internes contiennent des opérateurs, alors que les feuilles contiennent des valeurs (opérandes).

Par exemple, l'expression 3 * (( 5 + 1 )/2 ) sera représentée par l'arbre suivant :

Exercice :
a. Donner une fonction récursive pour évaluer une expression arithmétique sous la forme d'un arbre
binaire.
b. Une autre pour imprimer l’expression infixée parenthèsée.

13
Tas
Un tas, en anglais heap, (ou plus précisément un tas binaire) est une structure de données répondant
aux conditions suivantes :
• c'est un arbre binaire parfait
• il est ordonné en tas

On dit qu'un arbre est ordonné en tas lorsque la propriété suivante est vérifiée : les nœuds
sont ordonnés par leurs clés respectives, et :

Pour tous A et B nœuds de l'arbre tels que B soit un fils de A clé(A) ≥ clé(B)

Cette propriété implique que la plus grande clé soit située à la racine du tas. Ils sont ainsi très
utilisés pour implémenter les files à priorités car ils permettent des insertions en temps
logarithmique et un accès direct au plus grand élément. L'efficacité des opérations effectuée sur des
tas est très importante dans de nombreux algorithmes sur les graphes.

Le fait qu'un tas soit un arbre binaire parfait permet de le représenter d'une manière intuitive par un
tableau unidimensionnel.

• la racine de l'arbre est étiquetée 1,


• le fils gauche d'un nœud étiqueté n, est étiqueté 2n,
• le fils droit d'un nœud étiqueté n, est étiqueté 2n+1.

le cas du tas est l'un des rares où un vecteur dont le premier élément a l'indice 1 pourrait être
avantageux.

L'arbre (qui n’est pas un tas) :

est représenté par :

Un arbre binaire parfait a une représentation compacte sans trous dans le tableau:

14
Les deux opérations à définir sur un tas sont :

• L'insertion d'un nouveau nœud lors de la création du tas


• L’extraction du nœud qui réalise le minimum pour la relation d'ordre

Toute la difficulté consiste à maintenir la structure de tas entre deux opérations.

Pour insérer un nouveau nœud, rien de plus simple, il suffit de l'ajouter tout en bas de l'arbre et de le
faire remonter à sa place, c'est-à-dire à l'échanger avec son père tant que le poids de celui-ci (le
père) est supérieur à son propre poids (le nouveau nœud). Cette opération porte généralement le joli
nom de percolation. Au pire, on remonte le nouveau nœud jusqu'à la racine, donc l'algorithme
correspondant tourne en O(ln n).

Voici la fonction monter:

15
L'extraction paraît d'une simplicité déconcertante : il suffit d'extraire la racine du tas et le tour est
joué. Le problème, c'est que le tas obtenu est tout sauf un tas, puisqu'il n'a même plus de racine...
Une bonne solution est d'extraire la racine, de placer le dernier nœud du tas à sa place, et de faire
descendre ce dernier jusqu'à sa place dans le tas. Sur l'exemple, ceci donne :

Soit la fonction descente (tamiser) qui permet de descendre ce une clé nœud (position q) jusqu'à sa
place dans le tas.

16
Construction d’un tas :
Interpréter le tableau comme un tas (incorrect) puis rétablir l’ordre en partant des feuilles (vues
comme des tas corrects).
 Comme dans le tri fusion on part des tas préexistant (d’abord les feuilles)
 Et on construit ensuite l’arbre dont la racine est le père de ces feuilles… etc…

Exemple :

17

Vous aimerez peut-être aussi