corrigé - INRIA/pop-art

Université Joseph Fourier
RICM3
Polytech Grenoble
Année 2014–2015
Algorithmique avancée — TD no 2 : Arbres 2-3-4
1
Définition
Un arbre 2-3-4 est un arbre possédant les propriétés suivantes :
— chaque nœud de l’arbre contient n clés c1 ...cn , avec 1 ≤ n ≤ 3
— chaque nœud interne (qui n’est pas une feuille) a n + 1 fils a0 ...an
— les clés sont ordonnées dans l’arbre : ∀i ∈ {0, ..., n + 1}, c ∈ cles( ai ), ci ≤ c ≤ ci+1
— toutes les feuilles sont au même niveau dans l’arbre.
Voici un exemple d’arbre 2-3-4 :
11
3 7
0 1 2
2
4 5 6
18 21
8 9 10
12 13 17
19 20
22
Structure de donnée
Exercice 1.
Définir une structure de donnée permettant de représenter un arbre 2-3-4.
Un nœud doit contenir les informations suivantes :
— le nombre de clés (duquel on déduit le nombre de fils si le nœud n’est pas une feuille)
— les clés
— les pointeurs vers les fils
— un pointeur vers le nœud parent (pour pouvoir remonter dans l’arbre)
— enfin, il faut faire la différence entre les feuilles et les nœuds internes. Cela peut être fait par un booléen, ou en
définissant deux types distincts, puisqu’on verra par la suite qu’une feuille ne devient jamais un nœud interne
et vice-versa.
Exemple de définition :
Noeud : type <
nbcles : entier sur [1..3]
cles : tableau sur [1..3] d'entiers
ls : tableau sur [0..3] de pointeur de Noeud
parent : pointeur de Noeud
estFeuille : booléen
>
Arbre234 : type pointeur de Noeud
3
Recherche dans un arbre 2-3-4
Exercice 2.
Écrire l’algorithme de recherche d’une valeur dans un arbre 2-3-4.
Rechercher(X : entier, A : Arbre234)
si (A = nil) alors
retourner faux
sinon
{ recherche dans le tableau de clé }
i←1
tant que (i ≤ A↑.nbcles) et puis (A↑.cles[i] < X)
i←i+1
{ i = A↑.nbcles + 1 ou alors A↑.cles[i] ≥ X }
si i ≤ A↑.nbcles et puis A↑.cles[i] = X alors
retourner vrai
sinon
si A↑.estFeuille alors
retourner faux
sinon
retourner Rechercher(X, A↑.ls[i−1])
4
Insertion dans un arbre 2-3-4
Le principe de l’insertion dans un arbre 2-3-4 est le suivant :
1. recherche de la feuille dans laquelle la valeur doit être insérée ;
2. insérer la valeur dans le nœud si c’est possible ;
3. sinon, diviser ce nœud autour de la clé médiane en deux nœud contenant une clé, et insérer la clé médiane
dans le nœud parent.
Exercice 3.
Montrer l’arbre 2-3-4 obtenu après insertion :
1. des valeurs 14, 15 et 16 dans l’arbre donné ci-dessus
Pour la clé 14 :
(a) on descend dans l’arbre, la clé doit être insérée dans la feuille 12 13 17
(b) cette feuille est complète : on la divise autour de la valeur médiane (13)
13
12
17
(c) la clé 13 est insérée dans le nœud parent, et la clé 14 dans le fils droit :
11
3 7
0 1 2
4 5 6
13 18 21
8 9 10
12
14 17
19 20
22
La clé 15 est insérée dans la feuille 14 17 :
11
3 7
0 1 2
4 5 6
13 18 21
8 9 10
12
14 15 17
19 20
22
La clé 16 devrait être insérée dans la feuille 14 15 17 ; la clé 15 remonte donc dans la feuille 13 18 21 , ce
qui fait remonter la clé 18 à la racine de l’arbre :
11 18
3 7
0 1 2
13 15
4 5 6
8 9 10
12
14
21
16 17
19 20
22
2. d’une valeur dans un arbre dont tous les nœuds contiennent 3 clés.
Arbre initial (par exemple) :
12 23 34
1 5 7
13 15 20
24 28 29
37 39 42
Si on insère une clé (par exemple, 25), en remontant dans l’arbre on termine par la division de la racine elle-même,
et la création d’une nouvelle racine. L’arbre grandit par la racine !
23
12
1 5 7
28 34
13 15 20
24 25
29
37 39 42
Exercice 4.
Écrire l’opération d’insertion dans un arbre 2-3-4.
Insertion(X : entier, A : Arbre234)
{ pointeur sur arbre courant }
AC : Arbre234
{ ls gauche et ls droit après division }
FG, FD : Arbre234
{ valeur médiane après division }
mediane : entier
si (A = nil) alors
{ cas particulier : arbre initial vide }
allouer(A)
A↑.nbcles ← 1
A↑.estFeuille ← vrai
A↑.parent ← nil
A↑.cles[1] ← X
sinon
{ rechercher la feuille dans laquelle inserer }
AC ← A
tant que non AC↑.estFeuille
{ recherche dans le tableau de clé }
i←1
tant que (i ≤ AC↑.nbcles) et puis (AC↑.cles[i] < X)
i←i+1
{ i = AC↑.nbcles + 1 ou alors AC↑.cles[i] ≥ X }
AC ← AC↑.ls[i−1]
{ AC est une feuille }
si AC↑.nbcles ≤ 2 alors
{ cas simple : on insère dans la feuille }
{ décalage des valeurs }
i ← AC↑.nbcles
tant que (i > 0) et puis (AC↑.cles[i] > X)
AC↑.cles[i+1] ← AC↑.cles[i]
i←i−1
{ clé à insérer dans la case i+1 }
AC↑.cles[i+1] ← X
sinon
{ on divise la feuille en deux }
mediane ← AC↑.cles[2]
allouer(FD)
FD↑.estFeuille ← vrai
FD↑.nbcles ← 1
FD↑.cles[1] ← AC↑.cles[3]
FD↑.parent ← FG↑.parent
FG ← AC
FG↑.nbcles ← 1
{ insertion de X dans une feuille }
si X < mediane alors
AC ← FG
sinon AC ← FD
AC↑.cles[2] ← max(AC↑.cles[1], X)
AC↑.cles[1] ← min(AC↑.cles[1], X)
AC↑.nbcles ← 2
{ itération : on remonte dans l'arbre pour insérer médiane, FG et FD }
AC ← AC↑.parent
tant que (AC 6= nil) et puis (AC↑.nbcles = 3)
{ division du noeud AC }
{ A terminer ... }
{ mise a jour des valeurs de mediane, FG, FD }
{ n de l'itération : AC = nil ou AC↑.nbcles ≤ 2 }
si AC = nil alors
{ création d'une nouvelle racine }
allouer(AC)
AC↑.parent ← nil
AC↑.nbcles ← 1
AC↑.estFeuille ← faux
AC↑.cles[1] ← mediane
AC↑.ls[0] ← FG
FG↑.parent ← AC
AC↑.ls[1] ← FD
FD↑.parent ← AC
sinon
{ insertion de mediane dans le noeud }
{ décalage des valeurs }
i ← AC↑.nbcles
tant que (i > 0) et puis (AC↑.cles[i] > X)
AC↑.cles[i+1] ← AC↑.cles[i]
AC↑.ls[i+1] ← AC↑.ls[i]
i←i−1
{ clé à insérer dans la case i+1 }
AC↑.cles[i+1] ← mediane
AC↑.ls[i] ← FG
AC↑.ls[i+1] ← FD