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
© Copyright 2024