Vous êtes sur la page 1sur 84

Expos en structures de donnes

Titre : Les arbres

Les arbres

Plan
I. Gnralits II. Dfinitions III. Implmentation IV. Les fonctions de base sur la manipulation des arbres. V. Algorithmes de base sur les arbres binaires VI. Parcours d'un arbre VII. Oprations lmentaires sur un arbre VIII. Les autres types darbre
Les arbres n-aires Les arbres AVL Les arbres rouge-noir
2

Gnralits :
En informatique, un arbre est une structure de donnes rcursive gnrale, reprsentant un arbre au sens mathmatique. C'est un cas particulier de graphe qui n'a qu'une seule source et aucun cycle. Dans un arbre, on distingue deux catgories d'lments : * les feuilles; lments ne possdant pas de fils dans l'arbre , * les nuds internes; lments possdant des fils (sous-branches).

Gnralits

Les rpertoires sous la plupart des systmes d'exploitation actuels (Microsoft Windows, Unix dont Linux et Mac OS X ...) forment un arbre. Les arbres binaires Les arbres binaires de recherche Les arbres n-aires Les arbres AVL Les arbres rouge-noir

Les arbres

II. Dfinitions
Un arbre est une structure qui peut se dfinir de manire rcursive : un arbre est un arbre qui possde des liens ou des pointeurs vers d'autres arbres. Cette dfinition plutt trange au premier abord rsume bien la dmarche qui sera utilis pour raliser cette structure de donnes. On distingue deux grands types d'arbres : Arbres enracins Arbres non enracins

Les arbres

Arbres enracins
Un arbre enracin est un arbre hirarchique dans lequel on peut tablir des niveaux. Il ressemblera plus un arbre gnalogique tel qu'on le conoit couramment.

Les arbres

Arbres non enracins


Un arbre non enracin est un arbre ou il n'y a pas de relation d'ordre ou de hirarchie entre les lments qui composent l'arbre.

Vous remarquerez qu'il y a quivalence entre les deux arbres prcdents


7

Les arbres

II-B. Terminologie
Prcisons maintenant un peu plus les termes dsignant les diffrentes composantes d'un arbre. Tout d'abord, chaque lment d'un arbre se nomme un nud. Les nuds sont relis les uns aux autres par des relations d'ordre ou de hirarchie. Ainsi on dira qu'un nud possde un pre, c'est dire un nud qui lui est suprieur dans cette hirarchie. Il possde ventuellement un ou plusieurs fils. Il existe un nud qui n'a pas de pre, c'est donc la racine de l'arbre. Un nud qui n'a pas de fils est appel une feuille. Parfois on appelle une feuille un nud externe tout autre nud de l'arbre sera alors appel un nud interne.
8

Les arbres

II-B. Terminologie
Description des diffrents composants d'un arbre

Les arbres

II-C. Arit d'un arbre


On qualifie un arbre sur le nombre de fils qu'il possde. Ceci s'appelle l'arit de l'arbre. Un arbre dont les nuds ne comporteront qu'au maximum n fils sera d'arit n. On parlera alors d'arbre n-aires. Il existe un cas particulirement utilis : c'est l'arbre binaire. Dans un tel arbre, les nuds ont au maximum 2 fils. On parlera alors de fils gauche et de fils droit pour les nuds constituant ce type d'arbre. L'arit n'impose pas le nombre minimum de fils, il s'agit d'un maximum, ainsi un arbre d'arit 3 pourra avoir des nuds qui ont 0, 1, 2 ou 3 fils, mais en tout cas pas plus. On appelle degr d'un nud, le nombre de fils que possde ce nud.
10

Les arbres

II-D. Taille et hauteur d'un arbre.


On appelle la taille d'un arbre, le nombre de nud interne qui le compose. C'est dire le nombre nud total moins le nombre de feuille de l'arbre. On appel galement la profondeur d'un nud la distance en terme de nud par rapport l'origine. Par convention, la racine est de profondeur 0.

11

Les arbres

II-D. Taille et hauteur d'un arbre.


La hauteur de l'arbre est alors la profondeur maximale de ses nuds. C'est dire la profondeur laquelle il faut descendre dans l'arbre pour trouver le nud le plus loin de la racine. La hauteur d'un arbre est trs importante. En effet, c'est un repre de performance. Dans l'exemple le nud F est de profondeur 2 et le nud H est de profondeur 3.

La plupart des algorithmes que nous verrons dans la suite ont une complexit qui dpend de la hauteur de l'arbre.
12

Les arbres

II-E. Arbre localement complet, dgnr, complet.


Un arbre binaire localement complet est un arbre binaire dont chacun des nuds possdent soit 0 soit 2 fils. Ceci veut donc dire que les nuds internes auront tous deux fils. Dans ce type d'arbre, on peut tablir une relation entre la taille de l'arbre et le nombre de feuille. En effet, un arbre binaire localement complet de taille n aura n+1 feuille. L'arbre suivant est localement complet :

13

Les arbres

II-E. Arbre localement complet, dgnr, complet.


Un arbre dgnr (appel aussi filiforme) est un arbre dont les nuds ne possde qu'un et un seul fils. Cet arbre est donc tout simplement une liste chane. Ce type d'arbre est donc viter, puisqu'il n'apportera aucun avantage par rapport une liste chane simple. On a alors une relation entre la hauteur et la taille : un arbre dgnr de taille n a une hauteur gale n+1. L'arbre suivant est un arbre dgnr.

14

Les arbres

II-E. Arbre localement complet, dgnr, complet.


On appellera arbre binaire complet tout arbre qui est localement complet et dont toutes les feuilles ont la mme profondeur. Dans ce type d'arbre, on peut exprimer le nombre de nuds n de l'arbre en fonction de la hauteur h : n = 2^(h+1) -1.

15

Les arbres

III. Implmentation des arbres binaires:


Nous allons maintenant discuter de l'implmentation des arbres. Tout d'abord, dfinissons le nud. Un nud est une structure (ou un enregistrement) qui contient au minimum trois champs : Un champ contenant l'lment du nud, c'est l'information qui est importante. Cette information peut tre un entier, une chane de caractre ou tout autre chose que l'on dsire stocker. Les deux autres champs sont le fils gauche et le fils droit du nud. Ces deux fils sont en fait des arbres, on les appelle gnralement les sous arbres gauches et les sous arbres droit du nud. De part cette dfinition, un arbre ne pourra donc tre qu'un pointeur sur un nud.

16

Les arbres

III. Implmentation des arbres binaire en langage C :


Voici donc une manire d'implmenter un arbre binaire en langage C : typedef struct node * tree; struct node { TElement value; tree left; tree right; }; On remplacera le type TElement pas type ou la structure de donnes que l'on veut utiliser comme entit significative des nuds de l'arbre. De par cette dfinition, on peut donc aisment constater que l'arbre vide sera reprsent par la constante NULL.
17

Les arbres

III. Implmentation des arbres binaire en langage C :


Node :
Value right

left

Node :

Value
right

Node :

Value
right

left

left

NULL

NULL
18

Les arbres

III. Implmentation des arbres n-aires (Tableau en c ):


Si on veut reprsenter un autre type d'arbre, on a deux solutions : Soit nous connaissons l'avance le nombre maximal de fils des nuds (ce qui veut dire que l'on connat l'arit de l'arbre), Soit nous ne la connaissons pas. Dans le premier cas, nous pouvons utiliser un tableau pour stocker les fils. Ainsi pour un arbre d'arit 4 nous aurons l'implmentation suivante : typedef struct node * tree; struct node { TElement value; tree child[4]; };
19

Les arbres

III. Implmentation des arbres n-aires (Tableau en c ):


node : Value 5

Child

[0] [1] [2] [3]

/
node : Value 6

node : Value 4

Child

[0] [1] [2] [3]

/
node : Value 8

Child

[0] [1] [2] [3]

Child

[0] [1] [2] [3]

/
20

Les arbres

III. Implmentation des arbres n-aires (liste chaine ):


La deuxime solution consiste utiliser une liste chaine pour la liste des fils. typedef struct node * tree; typedef struct cell * list;

struct cell{ tree son; list next; }; struct node{ TElement value; list child; };
21

Les arbres

III. Implmentation des arbres n-aires (liste chaine ):


Ce type d'implmentation est dj un peu plus compliqu, en effet, nous avons une rcursivit mutuelle. Le type list besoin du type tree pour s'utiliser mais le type tree a besoin du type list pour fonctionner. Le langage C autorise ce genre de construction du fait que le type list et le type tree sont dfinis via des pointeurs (list est un pointeur sur une structure cell et le type tree est un pointeur sur une structure node). Nous nous contenterons dans cet expos que de la toute premire implmentation : l'implmentation des arbres binaires. Mais sachez qu'avec un peu d'adaptation, les algorithmes que nous allons voir sont parfaitement utilisables sur des arbres n-aires.

22

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


Afin de faciliter notre manipulation des arbres, nous allons crer quelques fonctions. La premire dtermine si un arbre est vide. Le pseudo code associ est simple : fonction EstVide( T : arbre ) renvoie un boolen si T == Null alors renvoyer vrai; sinon renvoyer faux; fin si La constante Null est diffrente suivant le langage que vous utiliserez, elle peut tre null, nul, nil ou encore NULL.
23

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


Voici ce que peut donner cette fonction en langage C :

bool EstVide( tree T) { return T = = NULL; }


On peut se demander tout simplement l'utilit d'une telle fonction. Tout simplement parce qu'il est plus simple de comprendre la signification de EstVide(T) plutt que T==NULL.

24

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


Maintenant, prenons deux fonctions qui vont nous permettre de rcuprer le fils gauche ainsi que le fils droit d'un arbre. Il faut faire attention un problme : le cas o l'arbre est vide. En effet, dans ce cas, il n'existe pas de sous arbre gauche ni de sous arbre droit. Pour rgler ce problme nous dcidons arbitrairement de renvoyer l'arbre vide comme fils d'un arbre vide. Voici ce que cela peut donner en pseudo code : fonction FilsGauche( T : arbre ) renvoie un arbre si EstVide(T) alors renvoyer arbre_vide; sinon renvoyer sous arbre gauche; fin si
25

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


En langage C, nous aurons donc : tree Left( tree T) { if ( EstVide(T) ) return NULL; else return T->left; } La fonction qui retourne le fils droit sera cod de la mme manire mais tout simplement au lieu de renvoyer le fils gauche, nous renvoyons le fils droit.

26

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


Passons une autre fonction qui peut nous tre utile : savoir si nous sommes sur une feuille. Voici tout d'abord le pseudo code : fonction EstUneFeuille(T : arbre) renvoie un boolen. { si EstVide(T) alors renvoyer faux; sinon si EstVide(FilsGauche(T)) et EstVide(FilsDroit(T)) alors renvoyer vrai; sinon renvoyer faux; fin si }
27

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


Ceci donne en C :

bool EstUneFeuille(tree T) { if (EstVide(T)) return false; else if EstVide(Left(T)) && EstVide(Right(T)) return true; else return false; }

28

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


Enfin, nous pouvons crer une dernire fonction bien que trs peu utile : Dterminer si un nud est un nud interne. Pour ce faire, deux mthodes: Soit on effectue le test classique en regardant si un des fils n'est pas vide, soit on utilise la fonction prcdente.

Le pseudo code utilisant la fonction prcdente : fonction EstNudInterne( T : arbre ) renvoie un boolen
si EstUneFeuille(T) alors renvoyer faux; sinon renvoyer vrai; fin si
29

Les arbres

IV. Les fonctions de base sur la manipulation des arbres :


On aura donc en C la fonction suivante :

bool EstNudInterne(tree T) { return ! EstUneFeuille(T) ; }

30

Les arbres

V. Algorithmes de base sur les arbres binaires :


Calcul de la hauteur d'un arbre Pour calculer la hauteur d'un arbre, nous allons nous baser sur la dfinition rcursive : - un arbre vide est de hauteur 0. - un arbre non vide a pour hauteur 1 + la hauteur maximale entre ses fils. De part cette dfinition, nous pouvons en dduire un algorithme en pseudo code : fonction hauteur ( T : arbre ) renvoie un entier si T est vide renvoyer 0 sinon renvoyer 1 + max (hauteur ( FilsGauche(T)) , hauteur(FilsDroit(T) ) fin si
31

Les arbres

V. Algorithmes de base sur les arbres binaires :


Ainsi ceci pourrait donner en C : unsigned hauteur (tree T){ if ( EstVide(T) ) return 0; else return 1 + max( hauteur(Left(T) ) , hauteur(Right(T) ) ); } La fonction max n'est pas dfinie c'est ce que nous faisons maintenant :

unsigned max(unsigned a,unsigned b){ return (a>b)? a : b ; }


32

Les arbres

V. Algorithmes de base sur les arbres binaires :


Calcul du nombre de nud Le calcul du nombre de nud est trs simple. On dfinit le calcul en utilisant la dfinition rcursive : - Si l'arbre est vide : renvoyer 0 - Sinon renvoyer 1 plus la somme du nombre de nuds des sous arbres. On aura donc le pseudo code suivant : fonction NombreNud( T : arbre ) renvoie un entier si ( EstVide( T ) ) renvoyer 0; sinon renvoyer 1 NombreNud(FilsDroit(T)); fin si

NombreNud(FilsGauche(T))

33

Les arbres

V. Algorithmes de base sur les arbres binaires :


On peut donc traduire cela en langage C : unsigned NombreNud(tree T) { if( EstVide(T) ) return 0; else return 1 + NombreNud(Left(T)) NombreNud(Right(T)); }

34

Les arbres

V. Algorithmes de base sur les arbres binaires :


Calcul du nombre de feuilles

Le calcul du nombre de feuille repose sur la dfinition rcursive :


-un arbre vide n'a pas de feuille.

- un arbre non vide a son nombre de feuille dfini de la faon suivante : - si le nud est une feuille alors on renvoie 1 - si c'est un nud interne alors le nombre de feuille est la somme du nombre de feuille de chacun de ses fils.

35

Les arbres

V. Algorithmes de base sur les arbres binaires :


Le pseudo code que nous pouvons en tirer :

fonction nombreFeuille (T : arbre) renvoie un entier


si T est vide alors renvoyer 0; sinon si T est une feuille alors renvoyer 1 sinon renvoyer nombrefeuille( FilsGauche(T) ) + nombrefeuille( FilsDroit(T) ); fin si

36

Les arbres

V. Algorithmes de base sur les arbres binaires :


Ce que peut donner en langage C :

unsigned nombreFeuille( tree T) { if( EstVide(T) ) return 0; else if ( EstUneFeuille(T) ) return 1; else return nombreFeuille(Left(T)) + nombreFeuille(Right(T)); }

37

Les arbres

V. Algorithmes de base sur les arbres binaires :


Nombre de nud internes :

Pour finir avec les algorithmes de base, nous allons calculer le nombre de nud interne, Cela repose sur le mme principe que le calcul du nombre de feuille. La dfinition rcursive est la suivante : - un arbre vide n'a pas de nud interne. - si le nud en cours n'a pas de fils alors renvoyer 0 - si le nud a au moins un fils, renvoyer 1 plus la somme des nuds interne des sous arbres.

38

Les arbres

V. Algorithmes de base sur les arbres binaires :


On en dduit le pseudo code correspondant :

fonction NombreNudInterne(T : arbre ) renvoie un entier


si EstVide(T) alors renvoyer 0 sinon si EstUneFeuille(T) alors renvoyer 0 sinon renvoyer 1 + NombreNudInterne(FilsGauche(T)) + NombreNudInterne(FilsDroit(T));

39

Les arbres

V. Algorithmes de base sur les arbres binaires :


Ce qui donne en langage C :

unsigned NombreNudInterne(tree T) { if (EstVide(T)) return 0; else if(EstUneFeuille(T)) return 0; else return 1 + NombreNudInterne(Left(T)) NombreNudInterne(Right(T)); }

40

Les arbres

VI. Parcours d'un arbre :


Nous allons dcouvrir des algorithmes de parcours d'un arbre. Cela permet de visiter tous les nuds de l'arbre et ventuellement appliquer une fonction sur ces nuds. Nous distinguerons deux types de parcours : le parcours en profondeur et le parcours en largeur.

Le parcours en profondeur permet d'explorer l'arbre en explorant jusqu'au bout une branche pour passer la suivante.
Le parcours en largeur permet d'explorer l'arbre niveau par niveau. C'est dire que l'on va parcourir tous les nuds du niveau un puis ceux du niveau deux et ainsi de suite jusqu' l'exploration de tous les nuds.

41

Les arbres

VI. Parcours d'un arbre : Parcours en profondeur


Parcours en profondeur Le parcours en profondeur de l'arbre suivant donne : 1,2,3,4,5,6,7,8,9,10

Parcours prfixe de l'arbre : 1-2-3-4-5-6-7-8-9-10 Ceci n'est en fait qu'un seul parcours en profondeur de l'arbre. Il s'agit d'un parcours d'arbre en profondeur gauche d'abord et prfixe.

42

Les arbres

VI. Parcours d'un arbre : en profondeur parcours prfixe


Les types de parcours infixe, suffixe et postfixe sont les plus importants, en effet chacun son application particulire. Nous verrons cela dans la suite Commenons par le parcours prfixe, celui ci traite la racine d'abord. procedure parcours_prof_prefixe(T : arbre)

si non EstVide(T) alors


traiter_racine(T); parcours_prof_prefixe(FilsGauche(T)); parcours_prof_prefixe(FilsDroit(T)); fin si
43

Les arbres

VI. Parcours d'un arbre : en profondeur parcours prfixe


La fonction (ou procdure) traiter_racine, est une fonction que vous dfinissez vous mme, il s'agit par exemple d'une fonction d'affichage de l'lment qui est la racine.

44

Les arbres

VI. Parcours d'un arbre : en profondeur parcours infixe


procedure parcours_prof_infixe(T : arbre)

si non EstVide(T) alors


parcours_prof_infixe(FilsGauche(T)); traiter_racine(T); parcours_prof_infixe(FilsDroit(T)); fin si

45

Les arbres

VI. Parcours d'un arbre : en profondeur parcours suffixe


procedure parcours_prof_suffixe(T : arbre)

si non EstVide(T) alors


parcours_prof_suffixe(FilsGauche(T)); parcours_prof_suffixe(FilsDroit(T)); traiter_racine(T); fin si

46

Les arbres

VI. Parcours d'un arbre :


Parcours en profondeur

Pour les trois fonctions de parcours, nous remarquerons que seul le placement de la fonction (ou procdure) traiter_racine diffre d'une fonction l'autre. Nous pouvons donc traiter ceci facilement, afin de ne pas avoir crire trois fois de suite le mme code.
le code que l'on peut avoir en C est :

47

Les arbres

VI. Parcours d'un arbre :


void DFS(tree T, char Type) { if( ! EstVide(T) ) { if( Type ==1 ) { /* traiter racine */ } DFS(Left(T), Type); if (Type == 2) { /* traiter racine */ } DFS(Right(T), Type); if( Type == 3) { /* traiter racine */ } } }
48

Les arbres

VI. Parcours d'un arbre :


partir de ce code, on peut facilement crer trois fonctions qui seront respectivement le parcours prfixe, le parcours infixe et le parcours suffixe.
void DFS_prefix(tree T) { DFS(T,1); } void DFS_infix(tree T) { DFS(T,2); } void DFS_postfix(tree T) { DFS(T,3); }
49

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


Nous allons aborder un type de parcours un peu plus compliqu, c'est le parcours en largeur. Il s'agit d'un parcours dans lequel, on traite les nuds un par un sur un mme niveau. On passe ensuite sur le niveau suivant, et ainsi de suite. Le parcours en largeur de l'arbre suivant est : 1 2 3 4 5 6 7 8 9 10.

50

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


le principe est le suivant, lorsque nous sommes sur un nud nous traitons ce nud (par exemple nous l'affichons) puis nous mettons les fils gauche et droit non vides de ce nud dans la file d'attente, puis nous traitons le prochain nud de la file d'attente. Au dbut, la file d'attente ne contient rien, nous y plaons donc la racine de l'arbre que nous voulons traiter. L'algorithme s'arrte lorsque la file d'attente est vide. En effet, lorsque la file d'attente est vide, cela veut dire qu'aucun des nuds parcourus prcdemment n'avait de sous arbre gauche ni de sous arbre droit. Par consquent, on a donc bien parcouru tous les nuds de l'arbre.

51

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


On en dduit donc le pseudo code suivant : procedure parcours_largeur(T : arbre) Creer_File_D'attente F ajouter(F,T); tant que F n'est pas vide faire X <- extraire(F); Traiter_racine(X); si non EstVide(FilsGauche(X)) alors ajouter(F,FilsGauche(X)); fin si si non EstVide(FilsDroit(X)) alors ajouter(F,FilsDroit(X)); fin si fin faire
52

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


Appliquons ce pseudo code l'arbre suivant :

53

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


Au tout dbut de l'algorithme, la file ne contient rien, on y ajoute donc l'arbre, la file d'attente devient donc :

Etat de la file d'attente

54

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


On traite la racine puis on y ajoute les fils droits et gauche, la file vaut donc :

Etat de la file aprs la premire itration

55

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


On traite le prochain nud dans la file d'attente. Celui ci a un fils droit, nous l'ajoutons donc la file d'attente. Cette dernire ne contient donc maintenant plus qu'un nud :

Etat de la file aprs la troisime itration

On traite ce nud. Celui ci n'ayant pas de fils, nous n'ajoutons donc rien la file. La file est dsormais vide, l'algorithme se termine.

56

Les arbres

VI. Parcours d'un arbre :Parcours en largeur (par niveau)


Voici donc le code en C : void WideSearch(tree T){ tree Temp; queue F; if( ! EstVide(T) ){ Add(F,T); while( ! Empty(F) ) { Temp = Extract(F); /* Traiter la racine */ if( ! EstVide(Left(Temp)) ) Add(F,Temp); if( ! EstVide(Right(Temp)) ) Add(F,Temp); } } }
57

Les arbres

VII. Oprations lmentaires sur un arbre :


On dfiniras par la suite lensemble des oprations lmentaires sur un arbre: Cration d'un arbre. Ajout d'un lment. Recherche dans un arbre. Suppression d'un arbre.

58

Les arbres

VII. Oprations lmentaires sur un arbre :


Cration d'un arbre

On distingue deux types de cration d'un arbre : cration d'un arbre vide, et cration d'un arbre partir d'un lment et de deux sous arbres.
Cration d'un arbre vide : Nous avons cre un arbre comme tant un pointeur, un arbre vide est donc un pointeur NULL. La fonction de cration d'un arbre vide est donc une fonction qui nous renvoie la constante NULL.

59

Les arbres

VII. Oprations lmentaires sur un arbre :


Cration d'un arbre

Cration d'un arbre partir d'un lment et de deux sous arbres : Il faut tout d'abord crer un nud, ensuite, on place dans les fils gauches et droits les sous arbres que l'on a passs en paramtre ainsi que la valeur associe au nud. Enfin, il suffit de renvoyer ce nud. En fait, ce n'est pas tout fait exact, puisque ce n'est pas un nud mais un pointeur sur un nud qu'il faut renvoyer. Mais nous utilisons le terme nud pour spcifier qu'il faut allouer un nud en mmoire.

60

Les arbres

VII. Oprations lmentaires sur un arbre :


Cration d'un arbre

le pseudo code correspondant la deuxime fonction est:


fonction CreerArbre(val : TElement, fg : arbre , fd : arbre ) renvoie un arbre X <- Allouer un nud en mmoire. X.valeur <- val; X.fils_gauche <- fg; X.fils_droit <- fd; renvoyer X;
61

Les arbres

VII. Oprations lmentaires sur un arbre :


Cration d'un arbre Ceci donnera donc en langage C :
tree CreerArbre(TElement val,tree ls, tree rs) { tree res; res = malloc(sizeof(*res)); if( res == NULL ) { fprintf(stderr,"Impossible d'allouer le nud"); return NULL; } res->value = val; res->left = ls; res->right = rs; return res; }
62

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment On distingue plusieurs cas : Soit on insre ds que l'on peut. Soit on insre de faon obtenir un arbre qui se rapproche le plus possible d'un arbre complet, soit on insre de faon garder une certaine logique dans l'arbre. Le premier type d'ajout est le plus simple, ds que l'on trouve un nud qui a un fils vide, on y met le sous arbre que l'on veut insrer. Cependant ce type de technique, si elle sert construire un arbre depuis le dbut un inconvnient : on va crer des arbres du type peigne. C'est dire que l'on va insrer notre lment tel que l'arbre final ressemblera ceci.

63

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment Ceci est trs embtant dans la mesure ou cela va crer des arbres de trs grande hauteur, et donc trs peu performant. Nanmoins, il peut arriver des cas o on a parfois besoin de ce type d'insertion. Dans ce cas, l'insertion se droule en deux temps : on cherche d'abord un fils vide, puis on insre. Ceci peut s'crire rcursivement : - si l'arbre dans lequel on veut insrer notre lment est vide, alors il s'agit d'une cration d'arbre. - sinon, si le nud en cours a un fils vide, alors on insre dans le fils vide. -sinon, on insre dans le fils gauche. Nous insrions du cot gauche, donc produire un peigne gauche, et si on insrait du cot droit, nous aurions un peigne droit.
64

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment le pseudo code correspondant scrivait :
procedure AjouteNud( src : arbre, elt : TElement ) si EstVide(src) alors src <- CreerArbre(elt,Null,Null); sinon si EstVide(FilsGauche(src)) alors src->gauche <- CreerArbre(elt,Null,Null); sinon si EstVide(FilsDroit(src)) alors src->droit <CreerArbre(elt,Null,Null); sinon AjouteNud(FilsGauche(src),elt); fin si fin si fin si
65

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment On peut traduire ceci en C :
void AjouteNud(tree src, TElement elt) { if ( src == NULL ){ src = CreerArbre(elt,NULL,NULL); } else if ( EstVide(Left(src)) ){ src->left = CreerArbre(elt,NULL,NULL); } else if ( EstVide(Right(src)) ){ src->right = CreerArbre(elt,NULL,NULL); } else{ AjouteNud(Left(src),elt); } }
66

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment Nous remarquons donc qu'ici nous crons des arbres qui ne sont pas performant (nous verrons ceci dans la recherche d'lment). Afin d'amliorer ceci, on peut ventuellement effectuer notre appel rcursif soit gauche soit droite et ceci de faon alatoire. Ceci permet un quilibrage des insertions des nuds mais ce n'est pas parfait. La solution pour obtenir des arbres les plus quilibrs possible consiste en l'utilisation d'arbres binaires dit rouge-noir. Ceci est assez compliqu et nous les verrons aprs. Cependant, nous allons voir un type d'arbre qui facilite les recherches : les arbres binaires de recherche.

67

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment Dans ce type d'arbre, il y a une cohrence entre les nuds, c'est dire que la hirarchie des nuds respecte une rgle. Pour un arbre binaire de recherche contenant des entiers, nous considrerons que les valeurs des nuds des sous arbres gauches sont infrieures la racine de ce nud et les valeurs des sous arbres droit sont suprieurs cette racine. Voici un exemple d'un tel arbre :

68

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment

Le principe d'insertion est simple : si on a un arbre vide, alors il s'agit de la cration d'un arbre. Sinon, on compare la valeur de l'lment insrer avec la valeur de la racine. Si l'lment est plus petit alors on insre gauche. sinon, on insre dans le fils droit de la racine.

69

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment Ajout dun nud dans un arbre binaire de recherche : procedure InsereArbreRecherche( src : arbre , elt : TElement ) si EstVide(src) alors src <- CreerArbre(elt,Null,Null); sinon si elt < src->val alors InsereArbreRecherche(FilsGauche(src), elt); sinon InsereArbreRecherche(FilsDroit(src), elt); fin si fin si
70

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment En C, void InsereArbreRecherche(tree src, TElement elt){ if( EstVide(src) ){ src = CreerArbre(elt,NULL,NULL); } else{ if (elt < src->value){ InsereArbreRecherche(Left(src), elt); } else{ InsereArbreRecherche(Right(src), elt); } } }
71

Les arbres

VII. Oprations lmentaires sur un arbre :


Ajout d'un lment Vous remarquerez que l'on insre les lments qui sont gaux la racine du cot droit de l'arbre. Autre remarque, vous constaterez que nous effectuons des comparaisons sur les entits du type TElement, ceci n'est valable que pour des valeurs numriques (entier, flottant et caractre). S'il s'agit d'autres types, vous devrez utiliser votre propre fonction de comparaison. (comme strcmp pour les chanes de caractre).

72

Les arbres

VII. Oprations lmentaires sur un arbre :


Recherche dans un arbre

Il y a principalement deux mthodes de recherche. Elles sont directement lies au type de l'arbre : si l'arbre est quelconque et si l'arbre est un arbre de recherche. Nos recherches se contenteront seulement de dterminer si la valeur existe dans l'arbre. Avec une petite adaptation, on peut rcuprer l'arbre dont la racine contient est identique l'lment cherch.

73

Les arbres

VII. Oprations lmentaires sur un arbre :


Recherche dans un arbre Dans un arbre quelconque; on cherche dans tous les nuds de l'arbre l'lment. Si celui ci est trouv, on renvoie vrai, si ce n'est pas le cas, on renvoie faux .Le pseudo code associ:
fonction Existe(src : arbre, elt : TElement) renvoie un boolen si EstVide(src) renvoyer faux sinon si src->value = elt alors renvoyer vrai sinon renvoyer Existe(FilsGauche(src),elt Existe(FilsDroit(src), elt); fin si sin si

ou

74

Les arbres

VII. Oprations lmentaires sur un arbre :


Recherche dans un arbre

Nous renvoyons un ou logique entre le sous arbre gauche et le sous arbre droit, pour pouvoir renvoyer vrai si l'lment existe dans l'un des sous arbres et faux sinon. Ce genre de recherche est correct mais n'est pas trs performante. En effet, il faut parcourir quasiment tous les nuds de l'arbre pour dterminer si l'lment existe.

75

Les arbres

VII. Oprations lmentaires sur un arbre :


Recherche dans un arbre

C'est pour cela que sont apparus les arbres binaires de recherche. En effet, on les nomme ainsi parce qu'ils optimisent les recherches dans un arbre. Pour savoir si un lment existe, il faut parcourir seulement une branche de l'arbre. Ce qui fait que le temps de recherche est directement proportionnel la hauteur de l'arbre.
L'algorithme se base directement sur les proprits de l'arbre, si l'lment que l'on cherche est plus petit que la valeur du nud alors on cherche dans le sous arbre de gauche, sinon, on cherche dans le sous arbre de droite.

76

Les arbres

VII. Oprations lmentaires sur un arbre :


Recherche dans un arbre le pseudo code correspondant :
fonction ExisteR( src : arbre, elt : TElement )
si EstVide(src) alors renvoyer faux sinon si src->valeur = elt alors renvoyer vrai; sinon si src->valeur > elt alors renvoyer ExisteR(FilsGauche(src) , elt ); sinon renvoyer ExisteR(FilsDroit(src) , elt ); fin si fin si fin si
77

Les arbres

VII. Oprations lmentaires sur un arbre :


Recherche dans un arbre :

Ce qui se traduit de la faon suivante en langage C : bool Exist(tree src , TElement elt) { if ( EstVide(src) ) return false; else if ( src->value == elt ) return true; else if ( src->value > elt ) return Exist(Left(src), elt); else return Exist(Right(src), elt); }
78

Les arbres

VII. Oprations lmentaires sur un arbre :


Suppression d'un arbre:

Par suppression de nud, nous entendrons suppression d'une feuille. En effet, un nud qui possde des fils s'il est supprim, entrane une rorganisation de l'arbre. Que faire alors des sous arbres du nud que nous voulons supprimer ?
La rponse cette question dpend normment du type d'application de l'arbre. On peut les supprimer, on peut rorganiser l'arbre (si c'est un arbre de recherche) en y insrant les sous arbres qui sont devenus orphelins. Bref, pour simplifier les choses, nous allons nous contenter de supprimer compltement l'arbre.
79

Les arbres

VII. Oprations lmentaires sur un arbre :


Suppression d'un arbre

L'algorithme de suppression de l'arbre est simple : On supprime les feuilles une par unes. On rpte l'opration autant de fois qu'il y a de feuilles. Cette opration est donc trs dpendante du nombre de nud.
En fait cet algorithme est un simple parcours d'arbre. En effet, lorsque nous devrons traiter la racine, nous appellerons une fonction de suppression de la racine. Comme nous avons dis plutt que nous ne supprimerons que des feuilles, avant de supprimer la racine, il faut supprimer les sous arbres gauche et droit. On en dduit donc que l'algorithme de suppression est un parcours d'arbre postfixe.
80

Les arbres

VII. Oprations lmentaires sur un arbre :


Suppression d'un arbre

Le pseudo code associ :


procedure supprime( src : arbre )

si non EstVide(src) alors supprime(FilsGauche(src)); supprime(FilsDroit(src)); supprimeNud(src); fin si

81

Les arbres

VII. Oprations lmentaires sur un arbre :


Suppression d'un arbre on en dduit le code en C :
void supprime(tree * src) { tree ls = Left(*src); tree rs = Right(*src);
if( ! EstVide(*src) ){ supprime( &ls ); supprime( &rs );

free( *src ); *src = NULL;


} }
82

Les arbres

VII. Oprations lmentaires sur un arbre :


Suppression d'un arbre

On peut se demander pourquoi nous passons par un pointeur sur un arbre pour effectuer notre fonction. Ceci est tout simplement du au fait que le C ne fait que du passage de paramtre par copie et par consquent si on veut que l'arbre soit rellement vide (ie : qu'il soit gal NULL) aprs l'excution de la fonction, il faut procder ainsi.
Les appels Left et Right au dbut ne posent aucun problme dans le cas o src vaut NULL. En effet, dans ce cas, les deux fonctions renverront le pointeur NULL.

83

Merci

84