Vous êtes sur la page 1sur 12

Niveau : 1èm Année ING

Matière : algorithmique
fondamentale
Semestre 1
Année Universitaire : 2021-2022

Correction TD 5 : Arbres binaires


EXERCICE 1 :
La hauteur d’un arbre est la plus grande profondeur que peut avoir un nœud quelconque de
l’arbre. Etant donné un arbre T représenté par chaînage.
1. Ecrire une fonction permettant de déterminer la hauteur de l’arbre T. Proposer une
solution récursive. Estimer sa complexité.
Type nœud = enregistrement
Fonction hauteur( A: arbre): entier Val : entier
Variables FG : pointeur vers nœud
Hg,Hd : entier FD : pointeur vers nœud
Début Fin enregistrement
Si estFeuille(A) ou A=NIL alors
Retourner 0 Type Arbre : pointeur vers nœud
Sinon
hg=0;
hd=0;
Si A^.FG<>NIL alors
Hg hauteur(A^.FG);
Finsi
Si A^.FD<>NIL alors
Hdhauteur(A^.FD);
Finsi
retourner (1+max(hg,hd));
Finsi
Fin

Fonction estFeuille(A : Arbre) : booléen


Début
Si (A<>NULL) et (A^.FG=NULL) et
(A^.FD = NULL)
alors retourner vrai
sinon retourner faux
Fin si
Fin
Complexité : T(N) = O(N) (avec N est le nombre de nœuds dans l’arbre = taille de l’arbre)
2. Ecrire une fonction permettant de déterminer le nombre de feuilles de l’arbre T.

Fonction NBFeuilles( A: arbre): entier


Début
Si A=NIL alors
Retourner 0
Sinon si estFeuille(A) alors
Retourner 1
Sinon
retourner (NBFeuilles( A^.FG)+ NBFeuilles( A^.FD));
Finsi
Fin

EXERCICE 2 :
Etant donné un arbre binaire composé de N nœuds. On suppose que l’arbre est équilibré.
Chaque nœud contient une valeur val, un pointeur FG vers le fils gauche et un pointeur
FD vers le fils droit. La racine est pointée par un pointeur « Arbre ».
1. Ecrire un algorithme récursif basé sur le paradigme « diviser-pour-régner »
permettant de déterminer la valeur maximale se trouvant dans l’arbre. Estimer sa
complexité.
Fonction maximum (A : Arbre) : entier
Variables
Maxg : entier
maxd : entier
Début
Si A = NULL alors retourner -99999 /*-*/ fin si
Si estFeuille(A)
alors retourner A^.val
Sinon
Maxg  maximum (A^.FG)
Maxd  maximum (A^.FD)
Retourner (max (A^.val, Maxg, Maxd)
Fin si
Fin si
Fin

Complexité : La complexité mesure le nombre de comparaisons en fonction du nombre de


nœuds qu’on suppose égal à N.
0 si N =1

T (N ) =  N 

2T   +1
  2 
a = 2 ; b= 2 ; k =0 ; a  b k ➔ 1er cas du théorème ➔ T ( N ) = ( N log b a ) = ( N )

2. Etant donné un tableau de N entiers complètements distincts. Ecrire une fonction «


construire » permettant de construire un arbre binaire de recherche en y plaçant les
éléments du tableau. Estimer sa complexité

On commence par écrire une fonction cree-arbre qui permet de créer un nœud de la valeur val
ayant deux fils gauche et droit, ensuite écrire une fonction permettant d’insérer un élément
dans un ABR et appeler cette dernière fonction autant de fois que la taille du tableau.
Fonction cree_arbre ( val ; entier , FG : arbre, FD :arbre ) : Arbre
Début
Allouer(A)
A^.val  val
A^.gauche FG
A^.droit FD
retourner A

2
Fin
Fonction insere ( A :arbre, v:entier) : arbre
Début
Si (A = NIL)
Retourner cree_arbre (v , NIL, NIL)
Sinon
Si ( v < A^.val) )
A^.FG  insere ( A^.FG , v )
Sinon
A^.FD  insere (A^.FD , v)
Finsi
Retourner A ;
Finsi
Fin
Fonction construire (T : tableau d’entiers, taille) :arbre
Variables
i :entier
Début
A:arbre
Pour i de 1 à taille faire
AInsere (A, T[i])
FinPour
Retourner A
Fin

/***************Version itérative de la fonction insere

Fonction insere-it (R : Arbre, v : entier) : Arbre


Variable Q, P : Arbre
Début
Q cree_arbre (v , NIL, NIL)
Si R = NULL alors Retourner (Q) fin si
PR
Tant que P <> NIL faire
Si v < P^. val alors
Si P^.FG = NIL alors P^.FG  Q ; Retourner (R)
Sinon PP^.FG
Fin si
Sinon Si P^.FD = NULL alors P^.FD  Q ; Retourner (R)
Sinon PP^.FD
Fin si
Fin si
Fin tant que
Fin

Complexité : Soit n la taille du tableau,


La complexité d’une opération d’insertion est en O(h), avec h la hauteur de l’arbre, on fera
alors n opérations d’insertion → O(nlogn)

3
EXERCICE 3 :
Etant donné un arbre binaire contenant N nœuds. Chaque nœud contient une valeur X, un
pointeur FG vers le fils gauche et un pointeur FD vers le fils droit.

1. Décrire les différents types de parcours d’un arbre enraciné.


On a deux types de parcours :
Parcours en profondeur : récursifs ou itératifs en utilisant une pile
* Infixe
* Préfixe
* Postfixe
Parcours en largeur : en utilisant une file

/****Parcous en profondeur***/ /****Parcours en largeur***/


Procédure ParcoursInfixe (A : Arbre)
Début Procédure ParcoursLargeur(A:
Si A <> NULL alors Arbre)
ParcoursInfixe (A^.FG) Début
Traiter (A) CréerFileVide(F)
ParcoursInfixe (A^.FD) Enfiler(F, A)
Fin si Tant que nonFileVide(F) faire
Fin ADéfiler(F)
**** Traiter (A)
Procédure ParcoursPrefixe (A : Si A→FG <>NULL alors
Arbre) Enfiler(F, A^.FG)
Début Fin si
Si A <> NULL alors Si A→FG <>NULL alors
Traiter (A) Enfiler(F, A^.FD)
ParcoursPrefixe (A^.FG) Fin si
ParcoursPrefixe (A ^.FD) Fin tant que
Fin si Fin
Fin
******
Procédure ParcoursPostfixe(A :
Arbre)
Début
Si A <> NULL alors
Parcours_postfixe (A^.FG)
Parcours_postfixe (A^.FD)
Traiter (A)
Fin si
Fin

2. Ecrire un algorithme pour vérifier si un arbre enraciné A est ou non un arbre


binaire de recherche. Expliquer votre méthode et préciser le type de parcours
choisi.

Fonction VerifABR(A : Arbre) : booléen


Début
Si (estVide(A) alors retourner vrai fin si

4
Si (estFeuille(A)) alors retourner vrai fin si
Si (estVide(A^.FG)) alors
Max_gauche  A^.val -1 //initialiser avec une valeur inférieure à A^.val
Sinon
Max_gauche  maximum (A^.FG) // calcule de la valeur maximale du SAG
Fin si
Si (Max_gauche >A^.val) alors retourner faux fin si
Si (estVide(A^.FD)) alors
Min_droite  A^.val + 1 //initialiser avec une valeur supérieure à A^.val
sinon
Min_droite  minimum (A^.FD) // calcule la valeur minimale du SAD
Fin si
Si (A^.val > Min_droite) alors retourner faux fin si
retourner ( VerifABR (A^.FG) et VerifABR (A^.FD));
Fin

//***
Complexité :
T(n)=O(n) dans le pire des cas (si l’arbre est un ABR)
T(n)=1 dans les meilleurs des cas (Exemple : la racine est inférieure à son fils gauche (
est la SAG est réduite à ce fils)

EXERCICE 4 :
Soit un arbre binaire où chaque nœud contient :
• soit une valeur entière si le nœud est terminal
• soit un opérateur arithmétique binaire (+ , - , * , /) un caractère parmi : ‘+’, ‘-‘,
‘*’, ‘/’ si le nœud n’est pas terminal.
Un nœud est terminal si ses deux fils sont nuls. Sachant que tous les opérateurs sont
binaires, chaque nœud admet ou bien 0 fils ou bien 2 fils.
Exemple : L’arbre ci-dessous représente l’expression 3*(10 +13) +10 /(18 - 23) .

* /

+ 10 -
3

10 13 18 23

1. Ecrire une fonction qui permet de vérifier si un arbre A est ou non complet

Rappel
• Un arbre binaire complet est un arbre binaire dont chaque sommet interne a
exactement deux fils.
• Tout sommet x d'un arbre binaire vérifie l'une des deux propriétés suivantes :

5
o x est une feuille,
o x a un sous arbre binaire dit gauche de racine G(x) et un sous arbre
binaire droit de racine D(x).
• Un arbre binaire parfait est un arbre binaire complet dans lequel toutes les feuilles
sont à la même hauteur dans l'arbre.
• Un arbre binaire de taille n a une hauteur moyenne log2(n).

//***********

Fonction verif_complet (A : Arbre) : booléen //parcours préfixe


Variabes :
Test_g, Test_d : booléen Fonction Degré (A : Arbre) : entier
Début Début
Si A <> Null alors Si A  Null
Si estFeuille(A) alors Retourner vrai Fin si Si A^.FG = NULL et A^.FD = NULL
Si Degré(A) = 1 alors Retourner faux Fin si Alors Retourner 0
Test_g  verif_complet(A^.FG) Fin si
Si Test_g alors Si A^.FG  NULL et A^.FD  NULL
Test_d verif_complet(A^.FD) Alors Retourner 2
Retourner (test_d) Fin si
Sinon retourner faux Retourner 1
Fin si Fin si
Fin si Retourner -1
Fin Fin
//*************
//*************
Fonction TestParfait (arbre B) : Booléen
Début
Retourner (EstParfait_rec (B, hauteur (B)))
Fin
//******
Fonction EstParfait_rec (B : arbre, h : entier) : Booléen
Début
Si B = NULL alors retourner (h = -1)
Sinon retourner (EstParfait_rec (B^.FG, h-1) et EstParfait_rec (B^.FD, h-1))
Fin si
Fin

2. Ecrire une fonction qui permet de calculer la valeur d’une expression représentée
par un arbre A et retourne sa valeur.
Nœud =enregistrement
Fonction Expression (A : Arbre) : Entier
op : caractère
Variables :
val : entier
Val_g, val_d : Entier
FG : pointeur vers Nœud
Début
FD : pointeur vers Nœud
Si EstVide(A) alors retourner 0 ;
Fin enregistrement
sinon Si EstFeuille(A) alors return (A^.val) ;
Arbre : pointeur vers nœud
sinon val_g  Expression(A^.FG)) ;
6
val_d  Expression(A^.FD) ;
Si A^.op = ‘+’ alors retourner (val_g + val_d) ;
sinon Si A^.op= ‘−‘ alors retourner val_g – val_d ;
sinon Si A^.op = ‘∗’ alors retourner vg ∗ vd ;
sinon retourner val_g/val_d ;
Fin si
Fin si
Fin si
Fin si
Fin si
Fin
3. Ecrire une fonction qui permet d’afficher en notation parenthésée l’expression
représentée par l’arbre A.

Procédure display(A : Arbre)


Début
Si (A<>NULL) alors
Si EstFeuille(A) alors écrire (A^.val) ;
Sinon écrire( ’(’) ;
display(A^.FG)) ;
écrire (A^.val) ;
display(A^.FD) ;
écrire( ’)’) ;
Fin si
Fin si
Fin

EXERCICE 5 :

1. Ecrire une fonction qui permet de chercher une valeur X dans un arbre binaire de
recherche A. La fonction retourne un pointeur sur le nœud et un pointeur sur son père
si X est trouvé. Si l’élément est trouvé dans la racine de l’arbre, le pointeur sur le père
sera nul.

Fonction chercher (A : arbre, var pere : arbre, X : entier) : Arbre


Début
Si (A = NULL) alors retourner NULL fin si
Si (X = A^.val) alors retourner A
Sinon // Si (X <> A^.val)
Si (X < A^.val)
alors pereA
AA^.FG
Retourner (chercher (A, pere, X)) ;
sinon pereA
AA^.FD
retourner chercher(A, pere, X) ;
Fin si

7
Fin si
Fin
1ier appel récursif poschercher(A, NULL, X)

2. Ecrire une fonction qui localise l’élément minimal d’un sous-arbre, le supprime de
l’arbre et retourne sa valeur.

Fonction supprimer_min (R : Arbre) : entier


Variables :
A, pere : Arbre
Val_min : entier
Début
AR;
Père  NULL
Tant que ( A^.FG <> NULL )
Père  A
A  A^.FG
Fin tant que
//sauvegarder la valeur de A
Val_min  A^.val

Si EstFeuille(A) alors Père^.FG  NULL


Sinon //A a un seul fils droit
Père^.FG A^.FD
Finsi
Free(A)
Retourner (Val_min )
Fin
3. Ecrire une fonction qui détruit dans un arbre A, un nœud identifié par un pointeur et
un pointeur sur son père.

Fonction supprimer_nœud (R : arbre, A, père : arbre) : Arbre


Début
Si ( A <> NULL)
Alors Si ( EstFeuille(A)
Alors Si père^.FG = A
Alors père^.FG  NULL
Sinon père^.FD  NULL
Fin si Fin si Fin si
Si Degré(A) = 1
Alors Si père^.FG = A
Alors Si A^.FG = NULL
Alors père^.FG  A^.FD
Sinon père^.FG A^.FG
Fin si
Sinon Si A^.FG=NULL alors père^.FD  A^.FD
Sinon père^.FD A^.FG

8
Fin si Fin si Fin si
Si Degre(A) = 2
Alors /*recherche de la plus petite valeur du sous arbre droite */
Val_min  supprimer_min(A) ;
A^.val = val_min ;
Finsi
Free(A)
Retourner R
Fin
4. Ecrire une fonction qui supprime une valeur X dans un arbre binaire de recherche
A.

Fonction spprimer_val(R : Arbre, x : entier) : Arbre


Début
Père NULL
AChercher(R, pere, x)
RSupprimer(R, A, pere)
Retourner R
Fin

EXERCICE 6 :
On voudrait écrire un vérificateur d’orthographe à l’aide d’un dictionnaire implémenté
sous la forme d’un arbre binaire de recherche tel que chaque nœud contient un mot du
dictionnaire et le mot contenu dans un nœud père est supérieur au mot contenu dans le
nœud du fils gauche et inférieur au mot contenu dans le nœud du fils droit. On suppose
que la longueur d’un mot ne dépasse pas 20 caractères.
1. Définir les structures de données (« nœud » et « Dictionnaire ») nécessaires pour
l’implémentation d’un dictionnaire sous la forme chaînée.

Type Chaine : tableau de [1..20] caractères


Type nœud = enregistrement
Mot : chaine
FG : pointeur sur nœud
FD : pointeur sur nœud
Fin enregistrement

Type Arbre : pointeur sur nœud

2. Vérifier que l’arbre DICO est un arbre binaire de recherche

Fonction VerifABR(DICO : Arbre) : Booléen


Début
Si (estVide(DICO) alors retourner vrai fin si
Si (estFeuille(DICO)) alors retourner vrai fin si
Si (estVide(DICO^.FG))
Alors Max_gauche  « »
Sinon Max_gauche  maximum (DICO^.GF)
Fin si

9
Si (strcmp(Max_gauche , DICO^.Mot) >0) alors retourner faux fin si
Si (estVide(DICO^.FD))
Alors Min_droite  « zzzz » ;
Sinon Min_droite  minimum (DICO^.FD)
Fin si
Si (strcmp(DICO^.Mot , Min_droite) > 0) alors retourner faux fin si
Retourner ( VerifABR (DICO^.FG) et VerifABR (DICO^.FD));
Fin

3. Ecrire une fonction « Ajouter » permettant d’ajouter un mot dans un dictionnaire


DICO.

Fonction Insérer_ABR_DICO (A : Arbre, x : chaine) : Arbre


Variable P,Q : Arbre
Début
QCréer_feuille(x)
Si A = NULL alors retourner (Q) fin si
PA
Tant que P <> NULL faire
Si strcmp(x , P^. Mot) <0)
Alors Si P^.FG = NULL
Alors P^.FG  Q
Retourner (A)
Sinon PP^.FG
Fin si
Sinon Si P^.FD = NULL
Alors P^.FD  Q
Retourner (A)
Sinon PP^.FD
Fin si
Fin si
Fin tant que
Fin

4. Ecrire une fonction « Existe » permettant de vérifier si un mot donné MOT existe
ou non dans un dictionnaire DICO. (Vous pouvez utiliser la fonction « strcmp »
pour comparer deux mots).

Fonction Existe(DICO : Arbre, MOT : chaine) : Booléen


Variable P : Arbre
Début
PDICO
Tant que P <> null faire
Si strcmp( MOT , P^.Mot) = 0)
Alors retourner vrai
Sinon Si strcmp( MOT , P^.Mot) < 0)
Alors P  P^.FG
Sinon P  P^.FD

10
Fin si Fin si
Fin tant que
Retourner Faux
Fin

5. Ecrire une fonction « Imprimer » permettant d’imprimer par ordre alphabétique tous
les mots du dictionnaire DICO.

Procédure LancerImpression (DICO : Arbre) //parcours infixe


Début
Si DICO<>NULL
Alors
LancerImprimer (DICO^.FG)
Imprimer (DICO^.Mot)
LancerImprimer (DICO^.FD)
Fin si
Fin

6. Sachant que le dictionnaire contient N mots. Quelle sont les complexités au pire et
au meilleur en nombre de comparaisons de mots de la fonction « Existe ».
T(n) = O(1) au meilleur lorsque l’élément est dans la racine
T(n) = O(n) si l’élément est le dernier et l’arbre n’est pas équilibré (arbre linéaire)
T(n) = O(Log n) si l’élément est le dernier et l’arbre est équilibré

7. Ecrire une fonction pour sortir le premier mot du dictionnaire DICO

Fonction Premier_Mot(DICO : Arbre)


Variables P, Pere : Arbre
Début
// trouver la valeur minimale dans l’arbre 1
P  DICO
Pere  null
Tant que P <> null faire
Pere  x;
P  P^.FG
Fin tant que
Retourner Pere
Fin

EXERCICE 7 :
Etant donné un arbre binaire de recherche A. Chaque nœud contient : val : une valeur entière,
FG : un pointeur sur le fils gauche et FD : un pointeur sur le fils droit. La structure «
Arbre » contient un seul pointeur « racine » pointant sur le premier nœud de l’arbre.
1. Ecrire une fonction permettant de compter et de retourner le nombre de nœuds (non
null) se trouvant dans l’arbre.
2. Ecrire une fonction permettant de supprimer le nœud contenant la plus petite valeur.
Quelle est sa complexité au pire, au meilleur et moyenne ?

11
Même travail que l’exercice 5

12

Vous aimerez peut-être aussi