Exercices pour s'entraîner☘
Prenez l'habitude de revenir vous entraîner régulièrement avec ces exercices tout au long de l'année. Ils sont généralement accompagnés de pistes et de leur solution pour vous permettre de progresser.
Rappels
- Chaque programme Python doit être sauvegardé sous forme de fichier texte
avec l'extension
.py
.
Enregistrez ce fichier dans le dossier[A04-Arbres]
avec le nom donné à l'exercice :ProgA04.51.py
,ProgA04.52.py
, etc... - Pour exécuter ce programme, il suffit de le sauvegarder puis d'appuyer
sur la touche
[F5]
.
Préambule☘
Téléchargez le module TAD_ArbresBin.py qui contient une implémentation d'arbre binaire et placez ce module dans votre répertoire de travail.
L'interface est celle donnée dans le cours :
Méthode/Opérations | Description |
---|---|
A = ArbreBin(rac, fg, fd) |
Initialisation d'un arbre binaire de racine rac ,
de fils gauche et fils droit les arbres fg et
fd Ces trois paramètres sont initialisés à None par
défaut |
est_vide(A) |
Renvoie True si l'arbre est vide, False sinon. |
racine(A) |
Renvoie la valeur de la racine de l’arbre A si cette
racine existe, None sinon. |
fils_gauche(A) |
Renvoie le sous-arbre (fils) gauche de A si
ce sous-arbre existe, un arbre vide sinon. |
fils_droit(A) |
Renvoie le sous-arbre (fils) gauche de A si
ce sous-arbre existe, un arbre vide sinon. |
Attention
Cette interface n'est pas orientée objet, elle s'utilise de la même manière que dans les exemples du cours et dans le TD.
Affichage
Pour visualiser cet arbre, vous avez deux possibilités :
- La fonction
print()
réalise un affichage en ligne de l'arbre A passé en argument. - La fonction
affiche_arbreBin()
réalise un affichage en deux dimensions de l'arbre A passé en argument. Cet affichage est effectué à l'aide du moduleturtle
(merci à Q. CHAMUSSY, enseignant dans l'académie de Lyon, pour l'idée originale de cette fonction).
ProgA04.51 - Automatiser l'implémentation de l'arbre☘
Il est parfois laborieux d'écrire toutes les instructions nécessaires à la mémorisation d'un arbre binaire avec l'interface proposée. Le but de cet exercice est donc d'automatiser le processus en utilisant un dictionnaire pour décrire l'arbre binaire.
Exemple
Voici comment décrire l'arbre dessiné ci-contre à l'aide d'un dictionnaire :
arbre3 = { 'r' : ('a', 'b'),
'a' : ('c', 'd'),
'c' : (None, 'h'),
'h' : (None, None),
'd' : ('i', 'j'),
'i' : (None, None),
'j' : ('m', None),
'm' : (None, None),
'b' : ('e', 'f'),
'e' : ('k', None),
'k' : (None, None),
'f' : (None, None)
}
Chaque clef du dictionnaire est l'étiquette d'un noeud de l'arbre binaire.
Chaque valeur de clef est constitué d'un couple (fils gauche ; fils droit)
dont chaque élément est soit None
, soit la clef suivante.
Inconvénient
Avec cette représentation, les noeuds doivent tous avoir des valeurs distinctes puisqu'ils sont des clefs du dictionnaire...
-
Complétez la définition récursive de la fonction
dict_to_arbre()
qui prend en paramètres un dictionnaire (dont la structure a été donnée ci-dessus) ainsi que la valeur de la racine et qui renvoie un arbre de typeArbreBin
.1 2 3 4 5 6
def dict_to_arbre(dico, racine): """ dico - dict, dictionnaire structuré par {clef : (fg, fd)} racine - valeur de a première clef à prendre en compte Sortie: ArbreBin - Arbre binaire généré par le dictionnaire """
Exemple
>>> arbre3 = dict_to_arbre(arbre3, 'r') >>> affiche_arbreBin(arbre3)
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14
def dict_to_arbre(dico, racine): """ dico - dict, dictionnaire structuré par {clef : (fg, fd)} racine - valeur de a première clef à prendre en compte Sortie: ArbreBin - Arbre binaire généré par le dictionnaire """ if racine is None: return racine if racine not in dico.keys() : return ArbreBin(racine) else: fils_gauche = dict_to_arbre(dico, dico[racine][0]) fils_droit = dict_to_arbre(dico, dico[racine][1]) return ArbreBin(racine, fils_gauche, fils_droit)
Remarque
La solution proposée permet de se contenter d'un dictionnaire dans lequel les feuilles de l'arbre n'ont pas besoin d'être des clefs :
arbre3 = { 'r' : ('a', 'b'), 'a' : ('c', 'd'), 'c' : (None, 'h'), 'd' : ('i', 'j'), 'j' : ('m', None), 'b' : ('e', 'f'), 'e' : ('k', None) }
-
Complétez la définition récursive de la fonction
arbre_to_dict()
qui prend en paramètres un arbre binaire et un dictionnaire initialisé àNone
et qui renvoie un arbre sous la forme de dictionnaire.1 2 3 4 5 6
def arbre_to_dict(arbre, dico=None): """ arbre - ArbreBin, arbre binaire dico - dict, dictionnaire qui représentera l'arbre binaire Sortie: dict - dictionnaire dico structuré par {clef : (fg, fd)} """
Exemple
On entre un arbre binaire de manière usuelle dans le
main
:1 2 3 4 5 6
a = ArbreBin(88) b = ArbreBin(57, a) c = ArbreBin(62) d = ArbreBin(74) e = ArbreBin(51, c, d) arbre4 = ArbreBin(94, e, b)
Si on affiche l'arbre, on obtient :
Puis on exécute les instructions suivantes dans la console :
>>> arbre4 = arbre_to_dict(arbre4) >>> for clef in arbre4.keys(): ... print(f"{clef} : {arbre4[clef]}") ... 94 : (51, 57) 51 : (62, 74) 62 : (None, None) 74 : (None, None) 57 : (88, None) 88 : (None, None)
Une piste
La variable
dico
ayant pour valeur par défautNone
, il faut tester cette valeur puis, si c'est nécessaire, initialiserdico
comme un dictionnaire vide.Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13
def arbre_to_dict(arbre, dico=None): """ arbre - ArbreBin, arbre binaire dico - dict, dictionnaire qui représentera l'arbre binaire Sortie: dict - dictionnaire dico structuré par {clef : (fg, fd)} """ if dico is None: dico={} if not est_vide(arbre): dico[racine(arbre)] = (racine(fils_gauche(arbre)), racine(fils_droit(arbre))) arbre_to_dict(fils_gauche(arbre), dico) arbre_to_dict(fils_droit(arbre), dico) return dico
ProgA04.52 - Arbre généré aléatoirement☘
Variante du programme précédent (pour les plus « paresseux »), pourquoi ne pas générer un arbre binaire aléatoirement ?
Voici un algorithme pour des nœuds de valeur entière :
- On génère aléatoirement la valeur entière du noeud entre deux entiers fixés à l'avance.
- On donne la probabilité d'apparition du fils gauche qui, lui aussi, est un arbre généré aléatoirement.
- On donne la probabilité d'apparition du fils droit qui, lui aussi, est un arbre généré aléatoirement.
Pour éviter d'avoir un arbre « sans fin » on ajoute un paramètre qui correspond à la hauteur maximale de l'arbre binaire ainsi généré.
-
Complétez la définition récursive de la fonction
arbreAlea()
qui permet de générer un arbre binaire aléatoire selon l'algorithme précédent et dont les nœuds sont des entiers.1 2 3 4 5 6 7 8 9
def arbreAlea(hauteur, proba, b_inf, b_sup): """ hauteur - int, hauteur maximale de l'arbre proba - float, probabilité d'apparition d'un sous-arbre (gauche ou droite) b_inf, b_sup - int, entiers tels que b_inf < b_sup Sortie: ArbreBin - Arbre binaire généré aléatoirement Les noeuds sont des entiers compris entre b_inf et b_sup hauteur est la hauteur maximale de l'arbre """
Exemple
On génère un arbre binaire de hauteur 5 (au maximum), dont les valeurs des nœuds sont des entiers compris entre
1
et100
et de hauteur 5 au maximum :>>> arbre5 = arbreAlea(5, 0.6, 1, 100) >>> affiche_arbreBin(arbre5)
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
def arbreAlea(hauteur, proba, b_inf, b_sup): """ hauteur - int, hauteur maximale de l'arbre proba - float, probabilité d'apparition d'un sous-arbre (gauche ou droite) b_inf, b_sup - int, entiers tels que b_inf < b_sup Sortie: ArbreBin - Arbre binaire généré aléatoirement Les noeuds sont des entiers compris entre b_inf et b_sup hauteur est la hauteur maximale de l'arbre """ if hauteur>0: racine = randint(b_inf, b_sup) fgauche, fdroit = None, None if random() < proba: fgauche = arbreAlea(hauteur-1, proba, b_inf, b_sup) if random() < proba: fdroit = arbreAlea(hauteur-1, proba, b_inf, b_sup) return ArbreBin(racine, fgauche, fdroit) else: return None
-
La fonction précédente génère un arbre qui peut souvent être » déséquilibré «. Vous pouvez améliorer en diminuant la probabilité d'apparition d'une branche selon le niveau (la profondeur) à laquelle vous cherchez à générer un sous-arbre aléatoire.
Pour ce faire, complétez la définition récursive de la fonctionarbreAlea2()
.1 2 3 4 5 6 7 8 9
def arbreAlea2(hauteur, niveau, b_inf, b_sup): """ hauteur - int, hauteur maximale de l'arbre niveau - int, niveau de génération d'un sous-arbre (gauche ou droite) b_inf, b_sup - int, entiers tels que b_inf < b_sup Sortie: ArbreBin - Arbre binaire généré aléatoirement Les noeuds sont des entiers compris entre b_inf et b_sup hauteur est la hauteur maximale de l'arbre """
Exemple
On génère un arbre binaire de hauteur 5 (au maximum), dont les valeurs des nœuds sont des entiers compris entre
1
et100
et de hauteur 4 au maximum :>>> arbre6 = arbreAlea2(4, 0, 1, 100) >>> affiche_arbreBin(arbre6)
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
def arbreAlea2(hauteur, niveau, b_inf, b_sup): """ hauteur - int, hauteur maximale de l'arbre niveau - int, niveau de génération d'un sous-arbre (gauche ou droite) b_inf, b_sup - int, entiers tels que b_inf < b_sup Sortie: ArbreBin - Arbre binaire généré aléatoirement Les noeuds sont des entiers compris entre b_inf et b_sup hauteur est la hauteur maximale de l'arbre """ if niveau<hauteur: racine = randint(b_inf, b_sup) fgauche, fdroit = None, None proba = (hauteur-niveau)/hauteur if random() < proba: fgauche = arbreAlea2(hauteur, niveau+1, b_inf, b_sup) if random() < proba: fdroit = arbreAlea2(hauteur, niveau+1, b_inf, b_sup) return ArbreBin(racine, fgauche, fdroit) else: return None
ProgA04.53 - Feuilles d'un arbre☘
Programmez la définition récursive de la fonction feuilles()
qui prend
en paramètre un arbre binaire et qui renvoie un tableau contenant les valeurs
des feuilles de cet arbre.
1 2 3 4 5 |
|
Exemples
Pour l'arbre vide :
>>> arbre = ArbreBin()
>>> feuilles(arbre)
[]
Pour l'arbre ci-contre :
1 2 3 4 5 6 |
|
>>> feuilles(arbre4)
[62, 74, 88]
Une solution
1 2 3 4 5 6 7 8 9 10 11 |
|
ProgA04.54 - Plus lourde branche☘
Programmez la définition récursive de la fonction valeurMaxBranche()
qui
prend en paramètre un arbre binaire dont les noeuds sont des nombres (entiers
ou flottants) et qui renvoie la valeur (somme des nœuds de la racine à une
feuille) de la plus grande branche.
1 2 3 4 5 |
|
Exemples
Pour l'arbre vide :
>>> arbre = ArbreBin()
>>> valeurMaxBranche(arbre)
0
Pour l'arbre ci-contre :
1 2 3 4 5 6 |
|
>>> valeurMaxBranche(arbre4)
239
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 |
|
ProgA04.55 - Nature des arbres☘
-
Un arbre est dit dégénéré lorsque tous ses « noeuds pères » n'ont qu'un seul enfant.
Programmez la définition récursive de la fonctionestDegenere()
qui prend en paramètre un arbre binaire et qui renvoieTrue
si cet arbre est dégénéré,False
sinon.1 2 3 4 5 6
def estDegenere(arbre): """ arbre - ArbreBin Sortie: bool - True si tous les nœuds internes n’ont qu’un seul enfant, False sinon """
Exemples
Pour l'arbre ci-contre :
On obtient :1 2 3 4 5 6
a = ArbreBin(88) b = ArbreBin(57, a) c = ArbreBin(62) d = ArbreBin(74) e = ArbreBin(51, c, d) arbre4 = ArbreBin(94, e, b)
>>> estDegenere(arbre4) False
Pour l'arbre ci-contre :
On obtient :1 2 3 4 5
a = ArbreBin(10) b = ArbreBin(57, a) c = ArbreBin(70, None, b) d = ArbreBin(86, c) arbre7 = ArbreBin(58, d)
>>> estDegenere(arbre7) True
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14
def estDegenere(arbre): """ arbre - ArbreBin Sortie: bool - True si tous les nœuds internes n’ont qu’un seul enfant, False sinon """ if est_vide(arbre): return True elif est_vide(fils_gauche(arbre)) and not est_vide(fils_droit(arbre)): return estDegenere(fils_droit(arbre)) elif est_vide(fils_droit(arbre)) and not est_vide(fils_gauche(arbre)): return estDegenere(fils_gauche(arbre)) else: return False
-
Un arbre est dit strict lorsque tous ses « noeuds pères » possèdent un nombre pair d'enfants (0 ou 2).
Programmez la définition récursive de la fonctionestStrict()
qui prend en paramètre un arbre binaire et qui renvoieTrue
si cet arbre est strict,False
sinon.1 2 3 4 5 6
def estStrict(arbre): """ arbre - ArbreBin Sortie: bool - True si chaque "noeud-père" possède un nombre pair d’enfants (0 ou 2), False sinon """
Exemples
Pour l'arbre ci-contre :
On obtient :1 2 3 4 5 6
a = ArbreBin(88) b = ArbreBin(57, a) c = ArbreBin(62) d = ArbreBin(74) e = ArbreBin(51, c, d) arbre4 = ArbreBin(94, e, b)
>>> estStrict(arbre4) False
Pour l'arbre ci-contre :
On obtient :1 2 3 4 5
b = ArbreBin(57) c = ArbreBin(62) d = ArbreBin(74) e = ArbreBin(51, c, d) arbre8 = ArbreBin(94, e, b)
>>> estStrict(arbre8) True
Une solution
1 2 3 4 5 6 7 8 9 10 11 12
def estStrict(arbre): """ arbre - ArbreBin Sortie: bool - True si chaque "noeud-père" possède un nombre pair d’enfants (0 ou 2), False sinon """ if est_vide(arbre): return True elif not est_vide(fils_gauche(arbre)) and not est_vide(fils_droit(arbre)): return estStrict(fils_gauche(arbre)) and estStrict(fils_droit(arbre)) else: return False
ProgA04.56 - Arbres égaux☘
Programmez la définition récursive de la fonction sontEgaux()
qui prend
en paramètre deux arbres binaires et qui renvoie True
si ces arbres sont
identiques (mêmes sous-arbres gauches et mêmes sous-arbres droits).
1 2 3 4 5 |
|
Exemples
Les arbres ci-dessous sont égaux :
Les arbres ci-dessous ne le sont pas :
Une solution
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|