Arbres en Python

publicité
M1 MEEF SD maths option info
Représentation d’arbres en Python
1
Plusieurs implémentations
1.1
Le minimum vital
Pour chacune de ces représentations, on pourra coder les fonctions :
noeud(x) crée un nouvel arbre formé d’un seul nœud de valeur x.
racine(a), fg(a) et fd(a) renvoient respectivement la racine, le fils gauche et le fisl droit de a.
modifR(a,x) affecte la nouvelle valeur x à la racine de a.
modifG(a,b) remplace le fils gauche de a par b.
1.2
Chaînage par indices
Dans tout langage proposant des tableaux, on peut construire des arbres sous forme d’un tableau à
3 colonnes :
indice
0
1
2
..
.
contenu du nœud
A
B
C
..
.
indice de son fils gauche
1
None
None
..
.
indice de son fils droit
2
None
None
..
.
On note que l’indice None représente un fils vide.
Il faut de plus mémoriser :
— l’indice de la racine (on pourrait décider que celle-ci est à l’indice 0 mais pour réorganiser l’arbre
ce n’est pas toujours pratique)
— le nombre de nœuds dans l’arbre (là encore pas indispensable, mais pratique pour savoir quelle
ligne est utilisable pour créer un nouveau nœud)
1.3
Construction « récursive »
Une idée très simple et qui peut suffire ici est de représenter chaque nœud par une liste à 3 éléments
[contenu, fg, fd] ou par une liste vide pour l’arbre vide.
C’est cependant assez limité :
— La taille étant fixée à 3, il serait plus logique d’utiliser un triplet, mais on perdrait le côté mutable
(si on fait du pur fonctionnel ça marche).
— Il devient vite illisible d’écrire a[0] pour le contenu du nœud, a[1] pour son fils gauche, etc.
Une option similaire mais plus agréable est l’utilisation d’un dictionnaire :
>>> a = {’val’:1, ’fg’:None, ’fd’:None}
On accède alors plus confortablement aux composantes du nœud par des « indices » nommés : a[’fg’]
est son fils gauche, etc.
1.4
Un détail un peu pénible
Quelle que soit la représentation choisie, on peut avoir de mauvaises surprises lorsqu’on cherche à
modifier l’arbre par effet de bord. En effet, s’il est possible de modifier n’importe quelle partie de l’arbre,
il est plus difficile de modifier le nœud racine. On rencontre notamment le problème lorsqu’on veut passer
d’un arbre vide à un arbre non vide et réciproquement.
Cela s’explique par le fait que toutes les parties de l’arbre sont embarquées par référence (le modèle
mémoire prédominant en Python), mais que le nœud racine, qui est passé en argument à une fonction,
ne peut lui-même être modifié.
1
Une solution est d’ajouter un niveau d’indirection pour la racine de l’arbre, ce qui permet de la
modifier dans les fonctions. Par exemple, on représente un arbre comme une liste à un seul élément [a]
qui est alors passée en argument aux fonctions. Cela reste lourd à utiliser et peu fiable ; on verra avec les
objets une façon plus lisible de réaliser cette indirection.
2
Applications
Plusieurs idées d’exercices pour se familiariser avec ces implémentations :
Exercice 1 Écrire des fonctions permettant de traduire un arbre depuis une de ces représentations vers
une autre.
Exercice 2 Écrire les parcours en profondeur et en largeur pour les représentations de votre choix.
Exercice 3 Utiliser l’une de ces représentations pour implémenter des arbres binaires de recherche.
2
Téléchargement