Les arbres binaires – Implémentations - Algo, la page

publicité
Les arbres binaires – Implémentations
Nathalie Junior Bouquet
mars 2014
Défnition :
Un arbre binaire est soit vide, soit de la forme B = <o, G, D>, où G et D sont des arbres binaires
disjoints et ’o’ est un nœud appelé racine.
e
u
c
u
q
l
q
o
e
n
Figure 1 – Arbre binaire
1
Le type algébrique abstrait
sorte
ArbreBinaire
utilise
Nœud, Élément
opérations
arbre-vide : → ArbreBinaire
<–, –, –> : Nœud × ArbreBinaire × ArbreBinaire → ArbreBinaire
racine
: ArbreBinaire → Nœud
g
: ArbreBinaire → ArbreBinaire
d
: ArbreBinaire → ArbreBinaire
contenu
: Nœud → Élément
préconditions
racine(B) est-défini-ssi B ̸= arbre-vide
g(B) est-défini-ssi B ̸= arbre-vide
d (B) est-défini-ssi B ̸= arbre-vide
axiomes
racine(<o, G, D>) = o
g(<o, G, D>) = G
d (<o, G, D>) = D
avec
G, D : ArbreBinaire
o
: Nœud
L’opération contenu permet d’associer à chaque Nœud de l’arbre une information de type Élément.
Un arbre dont les nœuds contiennent des éléments est dit arbre étiqueté.
1
Algorithmique
Les arbres binaires – Implémentations mars 2014
Prépas
Epita
On notera, abusivement, <r, G, D> l’arbre dont la racine contient l’élément r.
r
G
D
Figure 2 – Arbre binaire : une structure récursive
2
Implémentations
2.1
Implémentation dynamique
La représentation la plus naturelle reproduit la structure récursive (voir figure 2).
On utilise les pointeurs pour chaîner entre eux les nœuds. A chaque nœud on associe deux pointeurs,
vers les deux sous-arbres gauche et droit. L’arbre non vide est représenté par un pointeur sur le nœud
racine. Il a la valeur nul s’il est vide.
Nous utiliserons la plupart du temps des arbres étiquetés. Un champ supplémentaire cle représente
l’information contenue dans le nœud.
2.1.1
Le type
types
/* t_element */
t_arbreBinaire = ↑ t_noeudBinaire
t_noeudBinaire = enregistrement
t_element
cle
t_arbreBinaire
fg, fd
fin enregistrement t_noeudBinaire
B
e
c
u
NUL
u
q
l
NUL
NUL
q
e
o
n
NUL NUL
NUL NUL
NUL NUL
NUL NUL
Figure 3 – Représentation dynamique de l’arbre de la figure 1
2
Algorithmique
Les arbres binaires – Implémentations mars 2014
2.1.2
2.2
Prépas
Epita
Implémentation des opérations
Type abstrait : ArbreBinaire
Implémentation : type t_arbreBinaire
B : ArbreBinaire
B : t_arbreBinaire
arbre-vide
nul
contenu(racine(B))
B↑.cle
g(B)
B↑.fg
d(B)
B↑.fd
Implémentation statique : la numérotation hiérarchique
On peut utiliser un simple vecteur pour représenter un arbre binaire. Il suffit de stocker chaque valeur
à la position correspondant au numéro en ordre hiérarchique du nœud la contenant.
1
f
2
3
i
p
4
5
n
r
8
9
u
6
7
a
t
10
_
a
Figure 4 – Arbre binaire parfait + numérotation hiérarchique
constantes
MaxN = ...
types
/* t_element*/
t_vect_elts = MaxN t_element
variables
t_vect_elts
B
L’arbre de la figure 4 sera représenté par le tableau suivant :
1
f
2
p
3
i
4
n
5
r
6
a
7
t
8
u
9
_
10
a
L’utilisation de cette représentation est intéressante sur un arbre parfait (ou complet, comme celui de
la figure 8), car on peut limiter la taille du vecteur à celle de l’arbre. Par contre, sur un arbre quelconque,
la place occupée est moins optimale.
Représentation de l’arbre de la figure 1 :
1
e
2
c
3
u
4
u
5
l
6
q
7
8
3
9
q
10
e
11
12
o
13
n
...
...
Algorithmique
Les arbres binaires – Implémentations mars 2014
Prépas
Epita
La place utilisée sera d’autant moins optimisée que la hauteur de l’arbre sera élevée. Un arbre dégénéré
(ou filiforme) à n nœuds nécessitera un vecteur de taille 2n − 1 !
f
i
l
i
f
e
Figure 6 – Peigne droit
Figure 5 – Arbre filiforme
Représentation de l’arbre filiforme de la figure 5 :
1
f
2
3
i
...
...
7
l
...
...
14
i
...
...
29
f
...
...
15
g
...
...
30
i
31
n
58
e
...
...
Représentation du peigne droit de la figure 6 :
1
p
2
d
3
e
...
...
6
r
7
i
...
...
14
o
...
...
62
t
63
e
...
...
Utilisation
◦ La racine est en position 1 dans le vecteur.
◦ Si i est la position du nœud actuel alors :
◃ son fils gauche se trouve à la position 2i
◃ son fils droit se trouve à la position 2i + 1
◃ son père (sauf pour la racine de l’arbre),
se trouve à la position i div 2.
En pratique, il faut avoir une valeur particulière (∅)
pour remplacer la racine de l’arbre vide. Sauf si
l’arbre est parfait : la taille de l’arbre suffit !
4
Algorithmique
Les arbres binaires – Implémentations mars 2014
3
3.1
Prépas
Epita
Parcours d’un arbre binaire
Parcours en profondeur
Le parcours en profondeur main gauche consiste à
descendre dans l’arbre à gauche le plus loin possible.
Lorsqu’on ne peut plus descendre, on remonte d’un
niveau : si on vient de la gauche, on descend à droite,
sinon on remonte encore. . . Et on recommence. . .
La manière la plus simple d’envisager ce parcours est récursive. Il revient tout simplement à parcourir
le sous-arbre gauche, puis à parcourir le sous-arbre droit !
r
(1)
(3)
(2)
G
(1) : préfixe
D
(2) : infixe
(3) : suffixe
Figure 7 – Parcours en profondeur d’un arbre binaire
Lors du parcours en profondeur, chaque nœud est rencontré trois fois :
(1) avant de descendre sur le sous arbre-gauche : ordre préfixe,
(2) en remontant de la gauche, avant de descendre à droite : ordre infixe (ou symétrique),
(3) en remontant de la droite : ordre suffixe (ou postfixe).
Exemples : les trois ordres de traitement des nœuds d’un arbre induits lors d’un parcours en profondeur.
◦ L’ordre préfixe sur l’arbre de la figure 8 donnera : lui_est_complet
◦ L’ordre infixe sur l’arbre de la figure 4 donnera : un_parf ait
◦ L’ordre suffixe sur l’arbre de la figure 1 donnera : quelconque
l
u
c
s
i
_
e
t
o
_
m
l
p
e
Figure 8 – Arbre binaire complet
5
t
Algorithmique
Les arbres binaires – Implémentations mars 2014
Prépas
Epita
L’algorithme :
Avec l’implémentation statique :
Avec l’implémentation dynamique :
↓
algorithme procedure parcours_prof
parametres locaux
t_arbreBinaire
B
debut
si B[i] = ∅ alors
/* ∅ = "vide" */
sinon
prof_rec (B, 2*i)
prof_rec (B, 2*i+1)
fin si
fin algorithme procedure prof_rec
debut
si B = nul alors
/* terminaison */
sinon
/* traitement préfixe */
parcours_prof (B↑.fg)
/* traitement infixe */
parcours_prof (B↑.fd)
/* traitement suffixe */
fin si
fin algorithme procedure parcours_prof
3.2
algorithme procedure prof_rec
parametres locaux
t_vect_elts
B
entier
i
→
algorithme procedure parcours_prof
parametres locaux
t_vect_elts
B
debut
prof_rec (B, 1)
fin algorithme parcours_prof
Parcours en largeur
Le parcours en largeur ou par niveaux consiste à visiter les nœuds niveaux par niveaux (en général
de gauche à droite) : c’est l’ordre hiérarchique.
L’algorithme classique utilise une file :
– on enfile la racine de l’arbre
– tant que la file n’est pas vide :
– on récupère et on défile le premier élément de la file
– on enfile chacun de ses fils (s’ils existent), d’abord le gauche puis le droit.
L’algorithme : Avec l’implémentation dynamique
algorithme procedure parcours_largeur
parametres locaux
t_arbreBinaire
B
variables
t_file
f
debut
si B <> nul alors
f ← enfiler (B, file_vide())
faire
B ← defiler (f)
/* traitement B↑.cle */
si B↑.fg <> nul alors
f ← enfiler (B↑.fg, f)
fin si
si B↑.fd <> nul alors
f ← enfiler (B↑.fd, f)
fin si
tant que non est_vide (f)
fin si
fin algorithme procedure parcours_largeur
Pour la représentation statique, on peut envisager une implémentation sans file, en effectuant un
parcours séquentiel du vecteur (en passant les éventuelles valeurs ∅). Mais cela n’est réellement intéressant
que lorsque l’arbre est parfait ! Sinon, il suffit d’utiliser le parcours classique avec une file d’entiers, qui
contiendra les numéros en ordre hiérarchique des nœuds.
6
Téléchargement