Un arbre binaire est une structure de données abstraite formée d’un ensemble de nœuds organisés hiérarchiquement selon la définition par récurrence suivante :
Un arbre binaire est :
soit un arbre vide, noté $E$, ne contenant aucun nœud ;
soit un nœud, appelé racine, relié à exactement deux arbres binaires $g$ et $d$, respectivement appelés sous-arbres gauche et sous-arbre droit.
On note $T(r,g,d)$ l’arbre non vide dont la racine $r$ (on peut aussi indiquer l’étiquette de cette racine).
Vocabulaire et remarques
Un nœud dont les deux sous-arbres sont vides est appelé une feuille.
Un nœud qui n’est pas une feuille est un nœud interne.
Un arbre binaire est dit parfait si toutes les feuilles sont à la même profondeur.
La racine d’un arbre $T$ est le seul nœud de $T$ sans parent.
Propriétés
Position des sous-arbres
La position des sous-arbres gauche et droite est fondamentale : $$T(r,g,d) \neq T(r,d,g)$$
Taille d’un arbre
La taille d’un arbre est égale au nombre de nœuds qu’il comporte.
Le nombre de nœuds d’un arbre binaire $T$, noté $n(T)$, se calcule récursivement :
$n(E)=0$
$n(T)=1+n(g)+n(d)$
Profondeur d’un nœud
La profondeur d’un nœud $n$, notée $h(n)$, est sa distance à la racine.
La profondeur d’un nœud $n$, notée $h(n)$, est définie récursivement par :
$h(\text{racine})=0$
$h(n)=1+h(\text{parent de }n)$
La profondeur d’un nœud dans un arbre est donc le nombre d’arrêtes qu’il faut parcourir, depuis la racine, pour parvenir au nœud.
La définition de la profondeur d’un nœud peut varier ! Il est impératif de vérifier celle choisie avant tout travail.
Hauteur d’un arbre
La hauteur d’un arbre est la profondeur du nœud le plus éloigné de la racine.
La hauteur d’un arbre est aussi sa profondeur.
La hauteur d’un arbre binaire $T$, notée $h(T)$ est définie récursivement par :
$h(E)=-1$ si l’arbre est vide ;
$h(T)=1+\mathrm{max}(h(g), h(d))$
Remarque
On peut aussi définir la hauteur d’un nœud : la hauteur d’un nœud est la distance entre ce nœud et la feuille la plus profonde ayant une relation de descendance avec ce nœud.
La définition de la hauteur d’un arbre peut varier ! Il est impératif de vérifier celle choisie avant tout travail.
Relation entre le nombre de nœuds $n$ et la hauteur d’un arbre
Soit $T$ un arbre binaire, $n$ son nombre de nœuds et $h$ sa hauteur. Ces grandeurs sont liées par les relations suivantes :
$h + 1 \leq n \leq 2^{h+1} - 1$
le nombre de sous-arbres vides de $T$ est $n+1$.
Dans quel cas a-t-on $N = h+1$ ?
Réponse
On a $N = h+1$ lorsque chaque nœud ne possède qu’un seul nœud fils. Il s’agit donc d’une structure linéaire (liste).
Dans quel cas a-t-on $N = 2^{h+1} - 1$ ?
Réponse
On a $N = 2^{h+1} - 1$ lorsque l’arbre binaire est parfait, c’est à dire lorsque toutes les feuilles ont la même profondeur.
Spécification d’un arbre binaire
Créer un nœud (type) :
fonction : creer_nœud() $\longrightarrow$ retourne un nœud ;
fils_gauche et fils_droit sont automatiquement initialisés à None ;;
complexité : $O(0)$ (pas tout à fait vrai mais suffisant pour comprendre la suite).
Lire dans le champ valeur :
fonction : lire_valeur(n: Noeud[T]) $\longrightarrow$ T ;
complexité : 1 opération élémentaire.
Écrire dans le champ valeur :
fonction : modifier_valeur(n: Noeud[T], valeur: T) $\longrightarrow$ None ;
complexité : 1 opération élémentaire.
Lire dans le champ fils_gauche :
fonction : lire_filsG(n: Noeud[T]) $\longrightarrow$ Noeud[T] ;
complexité : 1 opération élémentaire.
Écrire dans le champ fils_gauche :
fonction : modifier_filsG(n: Noeud[T], m: Noeud[T]) $\longrightarrow$ None ;
complexité : 1 opération élémentaire.
Lire dans le champ fils_droit :
fonction : lire_filsD(n: Noeud[T]) $\longrightarrow$ Noeud[T] ;
complexité : 1 opération élémentaire.
Écrire dans le champ fils_droit :
fonction : modifier_filsD(n: Noeud[T], m: Noeud[T]) $\longrightarrow$ None ;
complexité : 1 opération élémentaire.
Tout comme on a désigné les listes chaînées par leur première cellule, on va désigner les arbres par leur nœud racine.
Implémentation de la spécification en Python à l’aide d’une classe
Il existe de nombreuses façons de d’implémenter la structure d’arbre en Python. Dans cette partie on va utiliser une classe.
Le paradigme de programmation utilisé ci-dessous est le paradigme impératif : on crée dans un premier temps une structure élémentaire qui ne sert qu’à stocker les différentes valeurs et on définit ensuite des fonctions qui manipulent cette structure.
La structure utilisée peut être :
Une classe qui définit trois attributs valeur, gauche et droit et aucune méthode ;
Une liste Python de trois éléments ;
Un dictionnaire comportant trois clés : valeur, gauche et droit.
On peut même imaginer utiliser un tableau ou une liste chaînée !
On ne créera pas de structure « arbre binaire » (accompagnée de méthodes) dans ce document.
Écrire le code de la classe Noeud respectant la spécification. Les trois attributs seront nommés valeur, gauche, droit.
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
from__future__importannotationsclassNoeud:"""
Structure de stockage pour créer des arbres binaires.
"""def__init__(self:Noeud,val:str,g:Noeud=None,d:Noeud=None)->None:""" Initialisation de l'objet. """self.valeur=valself.gauche=gself.droit=d
Écrire le code permettant de construire l’arbre :
graph TD
A("A") --- B("B")
B --- G(None)
B --- C("C")
C --- H(None)
C --- I(None)
A --- D("D")
D --- E(None)
D --- F(None)
Écrire le code de la fonction est_vide dont la spécification est :
1
2
3
4
defest_vide(n:Noeud)->bool:"""
Teste si un arbre est vide.
"""
Réponse
1
2
3
4
5
defest_vide(n:Noeud)->bool:"""
Teste si un arbre est vide.
"""returnn==None
Écrire le code de la fonction ajoute_filsG dont la spécification est :
1
2
3
4
defajoute_filsG(n:Noeud,valeur:str)->None:"""
Ajoute un noeud d'étiquette valeur comme fils gauche.
"""
Réponse
1
2
3
4
5
6
7
8
defajoute_filsG(n:Noeud,valeur:str)->None:"""
Ajoute un noeud d'étiquette valeur comme fils gauche.
"""ifest_vide(n.gauche):n.gauche=Noeud(valeur)else:raiseException("Le nœud possède déjà un fils gauche !")
Écrire le code de la fonction ajoute_filsD dont la spécification est :
1
2
3
4
defajoute_filsD(n:Noeud,valeur:str)->None:"""
Ajoute un noeud d'étiquette valeur comme fils droit.
"""
Réponse
1
2
3
4
5
6
7
8
defajoute_filsD(n:Noeud,valeur:str)->None:"""
Ajoute un noeud d'étiquette valeur comme fils droit.
"""ifest_vide(n.droit):n.droit=Noeud(valeur)else:raiseException("Le nœud possède déjà un fils droit !")
Écrire le code de la fonction est_feuille dont la spécification est :
1
2
3
4
defest_feuille(n:Noeud)->bool:"""
Teste si un noeud est une feuille.
"""
Réponse
1
2
3
4
5
defest_feuille(n:Noeud)->bool:"""
Teste si un noeud est une feuille.
"""returnest_vide(n.gauche)andest_vide(n.droit)
Écrire le code de la fonction degre_noeud dont la spécification est :
1
2
3
4
defdegre_noeud(n:Noeud)->int:"""
Détermine le degré du noeud passé en argument.
"""
Quelques algorithmes de manipulation des arbres binaires
Tous les algorithmes qui suivent sont récursifs.
Écrire le code de la fonction taille dont la spécification est :
1
2
3
4
deftaille(n:Noeud)->int:"""
Retourne le nombre de noeud dans l'arbre.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
deftaille(n:Noeud)->int:"""
Retourne le nombre de noeud dans l'arbre.
"""ifest_vide(n):# Cas de base : un arbre vide ne contient aucun noeud.return0else:# Recherche récursive dans les sous-arbres gauche et droit.taille_gauche=taille(n.gauche)taille_droit=taille(n.droit)# Calcul de la taillereturn1+taille_gauche+taille_droit
Écrire le code de la fonction profondeur dont la spécification est :
1
2
3
4
defprofondeur(n:Noeud)->int:"""
Retourne la profondeur de l'arbre.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
defprofondeur(n:Noeud)->int:"""
Retourne la profondeur de l'arbre.
"""ifest_vide(n):# Cas de base : si le nœud est vide, il n'est pas dans l'arbre.return-1else:# Recherche récursive dans les sous-arbres gauche et droit.arbre_gauche=profondeur(n.gauche)arbre_droit=profondeur(n.droit)# Détermine la profondeurreturn1+max(arbre_gauche,arbre_droit)
Écrire le code de la fonction nbre_feuilles dont la spécification est :
1
2
3
4
defnbre_feuilles(n:Noeud)->int:"""
Détermine le nombre de feuilles dans l'arbre.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defnbre_feuilles(n:Noeud)->int:"""
Détermine le nombre de feuilles dans l'arbre.
"""ifest_vide(n):# Cas de base : un arbre vide ne contient aucune feuillereturn0elifest_feuille(n):# Cas de base : on ajoute 1 au total dès qu'on rencontre une feuillereturn1else:# Recherche récursive dans les sous-arbres gauche et droit.nbre_feuilles_gauche=nbre_feuilles(n.gauche)nbre_feuilles_droit=nbre_feuilles(n.droit)# Calcul du nombre de feuillesreturnnbre_feuilles_gauche+nbre_feuilles_droit
Écrire le code de la fonction profondeur_noeud dont la spécification est :
1
2
3
4
defprofondeur_noeud(n:Noeud,val:str,prof:int=0)->int:"""
Détermine la profondeur du noeud de valeur val.
"""
defprofondeur_noeud(n:Noeud,val:str,prof:int=0)->int:"""
Détermine la profondeur du noeud de valeur val.
Retourne -1 si le noeud n'est pas dans l'arbre.
prof est la profondeur.
"""ifest_vide(n):# Cas de base : si le nœud est vide, il n'est pas dans l'arbre.return-1elifn.valeur==val:# On a trouvé le noeudreturnprofelse:# Recherche récursive dans les sous-arbres gauche et droit.# Incrémente la profondeurprof_gauche=profondeur_noeud(n.gauche,val,prof+1)prof_droite=profondeur_noeud(n.droit,val,prof+1)# Valeur retournéereturnmax(prof_gauche,prof_droite)
Exemple : recherche de la profondeur du nœud de valeur 3
Les nœuds passés en argument sont repérés par la notation 1n, 2n, …
Écrire le code de la fonction remplace dont la spécification est :
1
2
3
4
5
defremplace(n:Noeud,val_ini:str,val_fin:str)->None:"""
Cherche tous les noeuds de valeur val_ini et remplace ces
dernières par val_fin.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
defremplace(n:Noeud,val_ini:str,val_fin:str)->None:"""
Cherche tous les noeuds de valeur val_ini et remplace ces
dernières par val_fin.
"""ifest_vide(n):returnNoneelifn.valeur==val_ini:n.valeur=val_finelse:remplace(n.gauche,val_ini,val_fin)remplace(n.droit,val_ini,val_fin)
Écrire le code de la fonction ajoute dont la spécification est :
1
2
3
4
5
6
7
8
defajoute(n:Noeud,parent:str,enfant:str,gauche:bool)->None:"""
Ajoute un noeud de valeur enfant comme enfant du noeud de valeur
parent.
Le noeud enfant est placé par défaut à gauche.
HYPOTHÈSE : Le noeud de valeur parent existe.
"""
defajoute(n:Noeud,parent:str,enfant:str,gauche:bool)->None:"""
Ajoute un noeud de valeur enfant comme enfant du noeud de valeur
parent.
Le noeud enfant est placé par défaut à gauche.
HYPOTHÈSE : Le noeud de valeur parent existe.
"""ifest_vide(n):returnNoneelifn.valeur==parent:ifgaucheandnotest_vide(n.gauche):raiseException("Le noeud enfant gauche existe déjà !")ifnotgaucheandnotest_vide(n.droit):raiseException("Le noeud enfant droit existe déjà !")ifgauche:n.gauche=Noeud(enfant)else:n.droit=Noeud(enfant)else:ajoute(n.gauche,parent,enfant,gauche)ajoute(n.droit,parent,enfant,gauche)
Écrire le code de la fonction supprime dont la spécification est :
1
2
3
4
defsupprime(n:Noeud,val:str)->None:"""
Supprime le noeud de valeur val ainsi que tous ses descendants.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
defsupprime(n:Noeud,val:str)->None:"""
Supprime le noeud de valeur val ainsi que tous ses descendants.
"""ifest_vide(n):returnNoneelifest_feuille(n):returnNoneelifn.gauche.valeur==val:n.gauche=Noneelifn.droit.valeur==val:n.droit=Noneelse:supprime(n.gauche,val)supprime(n.droit,val)
Quelles sont les complexités des algorithmes implémentés dans cette section ?
Réponse
Les algorithmes parcourent une fois chaque nœud de l’arbre. La complexité est donc proportionnelle au nombre de nœuds.
Parcours en profondeur d’un arbre
Les fonction écrites jusqu’à présent parcourent en profondeur tous les nœuds des arbres sans que l’ordre dans lequel ce parcourt est effectué ait la moindre importance.
Si on souhaite donner un affichage de l’arbre, la façon dont on le parcourt prend alors une grande importance. On peut :
Parcourir le sous-arbre gauche, afficher la valeur du nœud racine puis enfin parcourir le sous-arbre droit. On parle d’un parcours infixe.
Afficher la valeur du nœud racine avant de parcourir le sous-arbre gauche puis le sous-arbre droit. On parle d’un parcours préfixe.
Parcourir le sous-arbre gauche, le sous-arbre droit puis afficher les valeurs. On parle d’un parcours suffixe.
Pour l’arbre ci-dessous, donner les parcours préfixe, infixe et suffixe.
Écrire le code des fonctions parcours_infixe, parcours_prefixe et parcours_suffixe dont les spécifications sont :
1
2
3
4
5
defparcours_infixe(n:Noeud)->str:"""
Parcourt le sous-arbre gauche, affiche la valeur du nœud puis parcourt le sous-arbre droit.
Retourne le parcours sous forme d'une chaîne de caractères.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
defparcours_infixe(n:Noeud)->str:"""
Parcourt le sous-arbre gauche, affiche la valeur du nœud puis parcourt le sous-arbre droit.
Retourne le parcours sous forme d'une chaîne de caractères.
"""ifest_vide(n):return""else:chaine=parcours_infixe(n.gauche)chaine+=str(n.valeur)chaine+=parcours_infixe(n.droit)returnchaine
1
2
3
4
5
defparcours_prefixe(n:Noeud)->None:"""
Parcourt le sous-arbre gauche, affiche la valeur du nœud puis parcourt le sous-arbre droit.
Retourne le parcours sous forme d'une chaîne de caractères.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
defparcours_prefixe(n:Noeud)->None:"""
Parcourt le sous-arbre gauche, affiche la valeur du nœud puis parcourt le sous-arbre droit.
Retourne le parcours sous forme d'une chaîne de caractères.
"""ifest_vide(n):return""else:chaine=str(n.valeur)chaine+=parcours_prefixe(n.gauche)chaine+=parcours_prefixe(n.droit)returnchaine
1
2
3
4
5
defparcours_suffixe(n:Noeud)->None:"""
Parcourt le sous-arbre gauche, affiche la valeur du nœud puis parcourt le sous-arbre droit.
Retourne le parcours sous forme d'une chaîne de caractères.
"""
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
defparcours_suffixe(n:Noeud)->None:"""
Parcourt le sous-arbre gauche, affiche la valeur du nœud puis parcourt le sous-arbre droit.
Retourne le parcours sous forme d'une chaîne de caractères.
"""ifest_vide(n):return""else:chaine=parcours_suffixe(n.gauche)chaine+=parcours_suffixe(n.droit)chaine+=str(n.valeur)returnchaine
Algorithmes itératifs — Parcours d’un arbre à l’aide d’une file ou d’une pile
Parcours en largeur d’un arbre
Décrire le fonctionnement de l’algorithme suivant et montrer qu’il réalise un parcours en largeur d’un arbre.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Fonction parcours_largeur(racine: Noeud):
File f= File() f.enfiler(racine) TantQue non f.est_vide():
n= f.defiler() print(n.valeur) Si non est_vide(n.filsG):
f.enfiler(n.filsG) FinSi
Si non est_vide(n.filsD):
f.enfiler(n.filsD) FinSi
FinTantQue
FinFonction
Implémenter en Python cet algorithme.
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
defparcours_largeur(n:Noeud)->str:""" Réalise le parcours en largeur d'un arbre binaire. """message=""f=File()f.enfiler(n)whilenotf.est_vide():n=f.defiler()message+=str(n.valeur)ifnotest_vide(n.gauche):f.enfiler(n.gauche)ifnotest_vide(n.droit):f.enfiler(n.droit)returnmessage
Parcours en profondeur d’un arbre
Décrire le fonctionnement de l’algorithme suivant et montrer qu’il réalise un parcours en profondeur d’un arbre.
De quel type de parcours en profondeur s’agit-il ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Fonction parcours_profondeur(racine: Noeud):
Pile p= Pile() p.empiler(racine) TantQue non p.est_vide():
n= p.depiler() print(n) Si non est_vide(n.filsG):
p.empiler(n.filsG) FinSi
Si non est_vide(n.filsD):
p.empiler(n.filsD) FinSi
FinTantQue
FinFonction
Réponse
L’algorithme semble realiser un parcours préfixe mais en explorant d’abord le sous-arbre droit au lieu d’explorer le sous-arbre gauche.
Modifier l’algorithme de telle sorte qu’il réalise le parcours préfixe.
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Fonction parcours_profondeur(racine: Noeud):
Pile p= Pile() p.empiler(racine) TantQue non p.est_vide():
n= p.depiler() print(n) Si non est_vide(n.filsD):
p.empiler(n.filsD) FinSi
Si non est_vide(n.filsG):
p.empiler(n.filsG) FinSi
FinTantQue
FinFonction
Implémenter en Python cet algorithme.
Réponse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
defparcours_prefixe_iter(n:Noeud)->str:"""
Réalise un parcours préfixe à l'aide d'une pile.
"""message=""p=Pile()p.empiler(n)whilenotp.est_vide():n=p.depiler()message+=str(n.valeur)ifnotest_vide(n.gauche):p.empiler(n.gauche)ifnotest_vide(n.droit):p.empiler(n.droit)returnmessage
Implémentation de la spécification en Python à l’aide d’une liste Python
Reprendre toutes les fonctions de ce document en n’utilisant pas la classe Noeud mais une liste à trois éléments. Le premier est la « valeur » du noeud, le second l’adresse du fils gauche, le troisième l’adresse du fils droit.
"""
Implémentation d'un arbre binaire en utilisant le
paradigme impératif et des listes.
Structure d'un noeud : [valeur, gauche, droit]
"""defest_vide(n:list[str,list,list])->bool:"""
Teste si un arbre est vide.
"""returnnisNonedefajoute_filsG(n:list[str,list,list],valeur:str)->None:"""
Ajoute un noeud d'étiquette valeur comme fils gauche.
"""ifest_vide(n[1]):n[1]=[valeur,None,None]else:raiseException("Le nœud possède déjà un fils gauche !")defajoute_filsD(n:list[str,list,list],valeur:str)->None:"""
Ajoute un noeud d'étiquette valeur comme fils droit.
"""ifest_vide(n[2]):n[2]=[valeur,None,None]else:raiseException("Le nœud possède déjà un fils droit !")defest_feuille(n:list[str,list,list])->bool:"""
Teste si un noeud est une feuille.
"""returnest_vide(n[1])andest_vide(n[2])defdegre_noeud(n:list[str,list,list])->int:"""
Détermine le degré du noeud passé en argument.
"""ifest_vide(n):raiseException("Le nœud est vide !")nbre=0ifnotest_vide(n[1]):nbre+=1ifnotest_vide(n[2]):nbre+=1returnnbredeftaille(n:list[str,list,list])->int:"""
Retourne le nombre de noeud dans l'arbre.
"""ifest_vide(n):return0else:return1+taille(n[1])+taille(n[2])defprofondeur(n:list[str,list,list])->int:"""
Retourne la profondeur de l'arbre.
"""ifest_vide(n):return-1else:return1+max(profondeur(n[1]),profondeur(n[2]))defnbre_feuilles(n:list[str,list,list])->int:"""
Détermine le nombre de feuilles dans l'arbre.
"""ifest_vide(n):return0elifest_feuille(n):return1else:returnnbre_feuilles(n[1])+nbre_feuilles(n[2])defparcours_pref(n:list[str,list,list])->str:""" Parcours en profondeur préfixe d'un arbre binaire. """ifest_vide(n):return""else:chaine=str(n[0])chaine+=parcours_pref(n[1])chaine+=parcours_pref(n[2])returnchainedefparcours_inf(n:list[str,list,list])->str:""" Parcours en profondeur infixe d'un arbre binaire. """ifest_vide(n):return""else:chaine=parcours_inf(n[1])chaine+=str(n[0])chaine+=parcours_inf(n[2])returnchainedefparcours_suf(n:list[str,list,list])->str:""" Parcours en profondeur infixe d'un arbre binaire. """ifest_vide(n):return""else:chaine=parcours_suf(n[1])chaine+=parcours_suf(n[2])chaine+=str(n[0])returnchaineif__name__=="__main__":arb=['A',['B',['C',None,None],['D',None,None]],['E',['F',None,None],['G',['H',None,None],None]]]print(est_vide(arb))print(f"Taille de l'arbre : {taille(arb)}")print(f"Profondeur (hauteur) de l'arbre : {profondeur(arb)}")print(f"Préfixe : {parcours_pref(arb)}")print(f"Infixe : {parcours_inf(arb)}")print(f"Suffixe : {parcours_suf(arb)}")
Implémentation de la spécification en Python à l’aide d’un dictionnaire
Reprendre toutes les fonctions de ce document en n’utilisant pas la classe Noeud mais un dictionnaire à trois clés : valeur, gauche et droit pour stocker la « valeur » du noeud, l’adresse du fils gauche et l’adresse du fils droit.
"""
Implémentation du type arbre binaire en utilisant
le paradigme impératif et des dictionnaires.
"""defest_vide(n:dict[str,dict,dict])->bool:"""
Teste si un arbre est vide.
"""returnn==Nonedefajoute_filsG(n:dict[str,dict,dict],val:str)->None:"""
Ajoute un noeud d'étiquette val comme fils gauche.
"""ifest_vide(n['gauche']):n['gauche']={'valeur':val,'gauche':None,'droit':None}else:raiseException("Le nœud possède déjà un fils gauche !")defajoute_filsD(n:dict[str,dict,dict],val:str)->None:"""
Ajoute un noeud d'étiquette valeur comme fils droit.
"""ifest_vide(n['droit']):n['droit']={'valeur':val,'gauche':None,'droit':None}else:raiseException("Le nœud possède déjà un fils droit !")defest_feuille(n:dict[str,dict,dict])->bool:"""
Teste si un noeud est une feuille.
"""returnest_vide(n['gauche'])andest_vide(n['droit'])defdegre_noeud(n:dict[str,dict,dict])->int:"""
Détermine le degré du noeud passé en argument.
"""ifest_vide(n):raiseException("Le nœud est vide !")nbre=0ifnotest_vide(n['gauche']):nbre+=1ifnotest_vide(n['droit']):nbre+=1returnnbredeftaille(n:dict[str,dict,dict])->int:"""
Retourne le nombre de noeud dans l'arbre.
"""ifest_vide(n):return0else:return1+taille(n['gauche'])+taille(n['droit'])defprofondeur(n:dict[str,dict,dict])->int:"""
Retourne la profondeur de l'arbre.
"""ifest_vide(n):return-1else:return1+max(profondeur(n['gauche']),profondeur(n['droit']))defnbre_feuilles(n:dict[str,dict,dict])->int:"""
Détermine le nombre de feuilles dans l'arbre.
"""ifest_vide(n):return0elifest_feuille(n):return1else:returnnbre_feuilles(n['gauche'])+nbre_feuilles(n['droit'])defparcours_pref(n:dict[str,dict,dict])->str:""" Parcours en profondeur préfixe d'un arbre binaire. """ifest_vide(n):return""else:returnstr(n['valeur'])+parcours_pref(n['gauche'])+\
parcours_pref(n['droit'])defparcours_inf(n:dict[str,dict,dict])->str:""" Parcours en profondeur infixe d'un arbre binaire. """ifest_vide(n):return""else:returnparcours_inf(n['gauche'])+str(n['valeur'])+\
parcours_inf(n['droit'])defparcours_suf(n:dict[str,dict,dict])->str:""" Parcours en profondeur infixe d'un arbre binaire. """ifest_vide(n):return""else:returnparcours_suf(n['gauche'])+parcours_suf(n['droit'])+\
str(n['valeur'])if__name__=="__main__":arb={'valeur':'A','gauche':None,'droit':{'valeur':'B','gauche':{'valeur':'C','gauche':None,'droit':None},'droit':{'valeur':'D','gauche':None,'droit':None}}}print(est_vide(arb))print(parcours_inf(arb))arb2={'valeur':'A','droit':None,'gauche':None}ajoute_filsG(arb2,"B")ajoute_filsD(arb2,"C")noeud_C=arb2['droit']ajoute_filsD(noeud_C,"D")ajoute_filsG(noeud_C,"E")print(f"Taille de l'arbre : {taille(arb2)}")print(f"Profondeur (hauteur) de l'arbre : {profondeur(arb2)}")print(parcours_pref(arb2))print(parcours_inf(arb2))print(parcours_suf(arb2))
À retenir
Un arbre binaire est un ensemble fini de nœuds, qui est soit vide (la valeur None est utilisée pour représenter l’arbre vide), soit structuré à partir d’un nœud particulier, appelé racine de l’arbre, et de deux sous-ensembles de nœuds formant récursivement un sous-arbre gauche et un sous-arbre droit.
Un arbre peut-être implémenté en Python avec un objet par nœud, d’une classe qui possède trois attributs : la valeur (ou étiquette) du nœud, le sous-arbre gauche et le sous-arbre droit. Il peut aussi être implémenté en utilisant une liste à trois éléments ou un dictionnaire à trois éléments.