les arbres généraux - uqac

23
1 Les arbres généraux 1. Introduction Comme mentionné au chapitre précédent, un arbre est général si les nombre d’enfants est variable pour les noeuds. Dans ce chapitre, nous allons, dans un premier temps, discuter des différentes implantations de ces arbres, et, dans un deuxième temps, étudier une classe particulière d’arbres généraux: les B-arbres. légende : - Subtree rooted at V : sous-arbre enraciné à V - Children of V: enfants de V - Siblings of V: soeurs/frères de V - Root : racine 2. Différentes implantations Avant de discuter des différentes implémentations d’un arbre général, regardons d’abord les différentes opérations que nous pourrions effectuer dans un arbre. La première étant d’accéder à la racine. Une autre est d’accéder aux enfants d’un noeud. Dans un arbre binaire, cette dernière opération est possible en utilisant des fonctions qui donnent des accès explicites à l’enfant gauche et enfant droit de ce noeud. Malheureusement, cette manière de procéder n’est pas possible dans le cas d’un arbre général, pour la simple raison que le nombre d’enfants n’est pas connu à priori. Une alternative est d’avoir une fonction qui prend en paramètre l’indice de l’enfant à visiter combinée à une autre fonction qui retourne le nombre d’enfants d’un noeud donnée est capable de supporter l’accès à un noeud donné ou traiter tous les noeuds. Cette manière de faire favorise l’utilisation d’un tableau (pourquoi?). En pratique, une implémentation basée sur les pointeurs est

Upload: others

Post on 31-Oct-2021

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Les arbres généraux - UQAC

1

Les arbres généraux 1. Introduction Comme mentionné au chapitre précédent, un arbre est général si les nombre d’enfants est variable pour les nœuds. Dans ce chapitre, nous allons, dans un premier temps, discuter des différentes implantations de ces arbres, et, dans un deuxième temps, étudier une classe particulière d’arbres généraux: les B-arbres.

légende : - Subtree rooted at V : sous-arbre enraciné à V

- Children of V: enfants de V - Siblings of V: soeurs/frères de V - Root : racine

2. Différentes implantations Avant de discuter des différentes implémentations d’un arbre général, regardons d’abord les différentes opérations que nous pourrions effectuer dans un arbre. La première étant d’accéder à la racine. Une autre est d’accéder aux enfants d’un nœud. Dans un arbre binaire, cette dernière opération est possible en utilisant des fonctions qui donnent des accès explicites à l’enfant gauche et enfant droit de ce nœud. Malheureusement, cette manière de procéder n’est pas possible dans le cas d’un arbre général, pour la simple raison que le nombre d’enfants n’est pas connu à priori. Une alternative est d’avoir une fonction qui prend en paramètre l’indice de l’enfant à visiter combinée à une autre fonction qui retourne le nombre d’enfants d’un nœud donnée est capable de supporter l’accès à un nœud donné ou traiter tous les nœuds. Cette manière de faire favorise l’utilisation d’un tableau (pourquoi?). En pratique, une implémentation basée sur les pointeurs est

Page 2: Les arbres généraux - UQAC

2

souvent préférée. Une autre alternative consiste à avoir accès à l’enfant le plus à gauche d’un nœud, et une autre fonction qui a accès au frère/sœur de droite d’un nœud. Ce qui suit montre la déclaration de classes pour les arbres généraux et leur nœuds basée sur cette approche. // classe nœud pour arbre général template <class Elem> class GTNode { public: GTNode(const Elem&); // Constructeur 1 GTNode(const Elem&, GTNode<Elem>*, GTNode<Elem>*, GTNode<Elem>*); // Constructeur 2 ~GTNode(); // Destructeur Elem value(); // Retourne la valeur du noeud bool isLeaf(); // VRAI si le nœud est une feuille GTNode* parent(); // Retourne le parent GTNode* leftmost_child(); // Retourne le premier enfant GTNode* right_sibling(); // Retourne le frère de droite void setValue(Elem&); // Assigne une valeur au noeud void insert_first(GTNode<Elem>* n); // insère comme premier enfant void insert_next(GTNode<Elem>* n); // insère comme frère de droite void remove_first(); // Enlève le premier enfant void remove_next(); // Enlève le frère de droite }; // Classe arbre general class GenTree { private void printhelp(GTNode*); // print helper function public: GenTree(); // Constructeur ~GenTree(); // Destructeur void clear(); // libère les noeuds GTNode* root(); // Retourne la racine void newroot(Elem, GTNode*, GTNode*); // fusionne deux arbres };

Page 3: Les arbres généraux - UQAC

3

Parcours d’un arbre général template <class Elem> void GenTree<Elem>:: printhelp(GTNode<Elem>* subroot) { if (subroot->isLeaf()) cout << "Leaf: "; else cout << "Internal: "; cout << subroot->value() << "\n"; for (GTNode<Elem>* temp = subroot->leftmost_child(); temp != NULL; temp = temp-> right_sibling()) printhelp(temp); }

Réponse : R A C D E B F 2.1. Implantation par le parent : Chaque nœud contient une valeur et un pointeur à son parent. Les pointeurs parents sont représentés par la position du parent dans le tableau. Cette représentation est souvent utilisée quand on veut savoir si deux objets sont dans le même arbre et aussi fusionner deux arbres en un seul arbre.

Page 4: Les arbres généraux - UQAC

4

?

bool Gentree::differ(int a, int b) { // les noeuds a et b sont-ils dans es arbres différents? GTNode* root1 = FIND(&array[a]); // trouve la racine de a GTNode* root2 = FIND(&array[b]); // trouve la racine de b return root1 != root2; // compare les raciness } class Gentree { // General tree: UNION/FIND private: GTNode* array; // Node array int size; // Size of node array GTNode* FIND(GTNode*) const; // trouver la racine public: Gentree(int); // Constructeur ~Gentree(); // Destructor void UNION(int, int); // fusionner bool differ(int, int); // TRUE si pas dans le même arbre }; Gentree::Gentree(int sz) { // Constructeur size = sz; array = new GTNode[sz]; // Créer le noeud tableau } Gentree::~Gentree() { // Destructeur delete [] array; // libérer le noeud tableau } bool Gentree::differ(int a, int b) { // les noeuds a et b sont-ils dans des arbres différents ? GTNode* root1 = FIND(&array[a]); // touver la racine de GTNode* root2 = FIND(&array[b]); // trouver la racine de b return root1 != root2; // Comparer les raciness }

Page 5: Les arbres généraux - UQAC

5

void Gentree::UNION(int a, int b) { // fusionner les sousarbres GTNode* root1 = FIND(&array[a]); // trouver la racine de a GTNode* root2 = FIND(&array[b]); // trouver la racine de b if (root1 != root2) root2->par = root1; // fusionner } GTNode* Gentree::FIND(GTNode* curr) const { while (curr->parent() != NULL) curr = curr->par; return curr; // At root } 2.2. Implantation par liste des enfants : La liste des nœuds est stockée dans un tableau. Chaque élément de ce tableau contient, la valeur du nœud, un pointeur vers son parent et un autre pointeur vers la liste de ses enfants de gauche vers la droite. Remarque: la combinaison d’arbre avec cette implantation est plutôt difficile à réaliser si chaque arbre est stocké dans un nœud tableau. Si les nœuds des arbres sont stockées dans un seul nœud tableau, alors ajouter l’arbre T comme un sous arbre du nœud R est fait simplement en joutant la racine de T à la liste des enfants de R 2.3. Implantation par enfant gauche – frère droit L’inconvénient de l’implantation ci-dessus est qu’il est difficile d’accéder au nœud droit d’un frère. La figure ci-dessous montre une implantation améliorée. Chaque élément du tableau stocke la valeur du nœud et des pointeurs à son parent, l’enfant le plus à gauche et le frère droit . Donc, chaque opération de ci-dessus peut être implantée en lisant la valeur directement du nœud.

Page 6: Les arbres généraux - UQAC

6

Si deux arbres sont stockés dans le même nœud tableau, alors ajouter un arbre comme un sous arbre de l’ordre exige la manipulation de pointeurs comme suit. 2.4. Implantation par des pointeurs (1) Les implantations qu’on vient de voir ci-dessous utilisent des tableaux pour stocker l’ensemble mble des nœuds de l’arbre. À l’inverse d’un arbre binaire, le nombre d’enfants que possède un noeud es variable. Si l’on désire procéder comme on a fait pour les arbres binaires, on est obligé de mettre une limite sur le nombre d’enfants que peut avoir un noeud donné et allouer des pointeurs pour exactement ce nombre d’enfants.

Union de deux arbres

Page 7: Les arbres généraux - UQAC

7

Deux inconvénients majeurs à cette implantation :

1. cette limite peut être telle que certains arbre ne pourront pas être implantée 2. une perte énorme d’espace étant donné que beaucoup de nœuds n’auront pas autant

d’enfants; ce qui va mettre ces pointeurs à NULL. 2.5. Implantation par des pointeurs (2) Une autre alternative, plus flexible, est de stoker une liste chaînée des pointeurs des enfants d’un nœud, comme illustré par la figure ci-dessous. Cette implantation et essentiellement similaire à celle de la liste des enfants vue précédemment, à la différence que les nœuds sont allouées d’une manière dynamique plutôt que de les stocker dans un tableau. 2.6. Implantation par des pointeurs (3) L’implantation, par tableau, de ‘enfant gauche – frère droit de ci-dessus’ peut être facilement adaptée à une implantation dynamique. En gros, on substitue un arbre binaire à un arbre général. Chaque nœud de cet arbre va pointer vers l’enfant le plus gauche et le frère de droite. Comme on peut le constater, à l’aide de cette implantation, nous traitons un arbre général comme un arbre binaire. La conversion se fait comme ci-dessous. Plus que cela, nous pouvons même adapter cette implantation à un ensemble d’arbres, puisque chaque racine peut être interprétée comme étant le

Page 8: Les arbres généraux - UQAC

8

frère droit de la racine d’un autre arbre. Cette implantation est de loin la plus utilisée pour les arbres généraux. 2.7. Conversion d’un arbre général en un arbre binaire Remarquez que l’implantation enfant gauche - frère droit essentiellement stocke un arbre binaire. Pour convertir d’une manière méthodique un arbre général en un arbre binaire, on procède comme suit. Pour chaque nœud de l’arbre 1. Laisser l’enfant gauche comme tel 2. Supprimer les autres enfants de ce nœud et les chaîner à l’enfant gauche Exemple : un ensemble d’arbres généraux convertis en un arbre binaire

template <class Elem> class GTNode { private: Elem element; GTNode<Elem>*parent; // Parent GTNode<Elem>* leftchild; // Premier fils - fils gauche GTNode<Elem>* rightsib; // Frère de droite public: GTNode(const Elem&); // Constructeur GTNode(const Elem&, GTNode<Elem>*, GTNode<Elem>*, GTNode<Elem>*); ~GTNode(); // Destructeur Elem value(); // Retourne la valeur du noeud bool isLeaf(); // VRAI si le nœud est une feuille GTNode* parent(); // Retourne le parent GTNode* leftmost_child(); // Retourne le premier enfant GTNode* right_sibling(); // Retourne le frère de droite void setValue(Elem&); // Assigne une valeur au noeud

Page 9: Les arbres généraux - UQAC

9

void insert_first(GTNode<Elem>* n); // insère comme premier enfant void insert_next(GTNode<Elem>* n); // insère comme frère de droite void remove_first(); // Enlève le premier enfant void remove_next(); // Enlève le frère de droite }; template <class Elem> GTNode<Elem>::GTNode(const Elem& value) { parent = leftchild = rightsib = NULL; element = value; } template <class Elem> GTNode<Elem>::GTNode(const Elem& value, GTNode<Elem>* par, GTNode<Elem>* leftc, GTNode<Elem>* rights) { element = value; parent = par; leftchild = leftc; rightsib = rights; } template <class Elem> GTNode<Elem>::~GTNode() { } template <class Elem> Elem GTNode<Elem>::value() { return element; } template <class Elem> bool GTNode<Elem>::isLeaf() { return leftchild == NULL; } template <class Elem> GTNode<Elem>* GTNode<Elem>::parent() { return rent; } template <class Elem> GTNode<Elem>* GTNode<Elem>::leftmost_child() { return leftchild; } template <class Elem> GTNode<Elem>* GTNode<Elem>::right_sibling() { return rightsib; } template <class Elem> void GTNode<Elem>::setValue(Elem& value) { element = value; } template <class Elem> void GTNode<Elem>::insert_first(GTNode<Elem>* n) { n->rightsib = leftchild; n->parent = this; leftchild = n; }

Page 10: Les arbres généraux - UQAC

10

template <class Elem> void GTNode<Elem>::insert_next(GTNode<Elem>* n) { n->rightsib = rightsib; n->parent = parent; rightsib = n; } template <class Elem> void GTNode<Elem>::remove_first() { if (leftchild == NULL) return; GTNode<Elem>* temp = leftchild; leftchild = leftchild->rightsib; delete temp; // pas bon – perd son sous arbre! } template <class Elem> void GTNode<Elem>::remove_next() { if (rightsib == NULL) return; GTNode<Elem>* temp = rightsib; rightsib = rightsib->rightsib; delete temp; // pas bon -- Perd son sous-arbre! } 2.8. Implantation séquentielle On considère dans cette section une approche complètement différente pour implanter des arbres. Le but est de stocker une série de valeurs des nœuds avec un minimum d’informations nécessaire à la reconstruction de l’arbre. Cette approche est connue sous le nom de implantation séquentielle des arbres. Elle a l’avantage de sauvegarder de l,espace car aucun pointeur n’est sauvegardé. Toutefois, son désavantage est de ne pouvoir accéder un nœud qu’après avoir traité tous les nœud qui apparaissent avant ce nœud dans la liste. Autrement dit, l’avantage que nous avons eu sur toutes les implantations discutées dans le chapitres précédent est perdue, à savoir un accès en O(log n). Cette implantation est idéale pour archiver des fichiers dans un disque pour un usage ultérieur car elle sauvegarde de l’espace, et l’arbre peut être reconstruit par la suite. La méthode consiste à énumérer les nœuds dans l’ordre qu’ils apparaissent lors d’un recherche en pré fixe.

Page 11: Les arbres généraux - UQAC

11

Exemple: Pour les arbres binaires, on utilise un symbole / pour indiquer un lien nul:

AB/D//CEG///FH//I//

AB/D//CEG///FH//I// Pour reconstruire l’arbre, on commence par mettre le nœud A comme racine. L’enfant gauche de A est B. L’enfant gauche de B est NULL. Donc le nœud D doit être l’enfant droit de B. Le nœud D a deux enfant NULL,. Donc le nœud C l’enfant droit de A, … etc. Pour montrer la difficulté d’utiliser cette implantation, considérons la recherche de l’enfant droit de la racine. Nous devons traiter avant tous les noeuds du sous-arbre gauche de cette racine. Cette implantation doit reconnaître quand un nœud est une feuille ou interne. Une manière de procéder est de lister explicitement un nœud : feuille ou interne.

A’B’/DC’E’G/F’HI

Cette approche nécessite un bit additionnel pour indiquer si le nœud est une feuille ou interne. Cependant, stoker n bits est beaucoup que de stocker n valeurs NULLS Implémenter les arbres généraux à l’aide de l’implémentation séquentielle nécessite plus d’informations à inclure dans un nœud : feuille ou interne et nombre d’enfants. Dans l’exemple suivant, le symbole parenthèse ) indique la fin de la liste d’enfant d’un noeud. Toutes les feuilles

Page 12: Les arbres généraux - UQAC

12

sont suivies d’un ). Une feuille qui est aussi le dernier enfant pour ses parents va indiquer ceci par deux ou plus symboles ) RAC)D)E))BF)))

Page 13: Les arbres généraux - UQAC

13

3. Les B-Arbres Parmi les les arbres génaux, on en distingue une classe particulière qui est celle des B-arbres.

3.1. Introduction

Les algorithmes de recherche, sur les très grandes collections de données, sont très différents de tout les algorithmes précédents. Tous les algorithmes précédents étaient dédiés à une utilisation en mémoire primaire.

La recherche dans de gros fichier sur disque ne peut donc pas utiliser les algorithmes précédents, pourtant très rapides. Étrangement, les algorithmes utilisés pour ce genre de tâches, peuvent retrouver un élément de données sur un disque d'une capacité allant jusqu'a 3 milliards de mots en seulement 2 ou 3 accès disques.

La mémoire secondaire est considérée comme beaucoup plus lente que la mémoire principale et comme étant paginée; c'est a dire découpée en pages pouvant contenir chacune un certain nombre d'éléments. La page est l’unité de transfert entre la mémoire centrale et le disque. Un seul accès au disque prend environ autant de temps que 200 000 instructions. La structure de données que nous introduisons ci-dessus est utilisée dans le but de réduire le nombre d’accès au disque.

Un B-arbre d'ordre m est un arbre de recherche formé de noeuds qui peuvent chacun contenir jusqu'a 2m éléments. Chaque noeud est dans une page différente du disque, et la complexité des algorithmes de recherche, adjonction et suppression d'un élément parmi n, est comptée en nombre d'accès à la mémoire secondaire est, comme on le verra plus loin, en O(logm n).

Définition: Un B-arbre d'ordre m est un arbre ayant les propriétés suivantes

• Chaque nœud a un maximum de 2m et un minimum de m éléments, excepté la racine qui peut contenir un minimum de un élément.

• Un noeud est soit une feuille (sans enfant) ou un noeud interne avec k éléments et k+1 enfants ( mkm 2≤≤ )

• If kXXX ,...,, 21 sont ces noeuds et kPPP ,...,, 10 les enfants, alors :

- 0P contient les éléments ≤ 1X

- 1P contient les éléments > 1X et ≤ 2X

- …………………

- iP contient les éléments > 1−iX et iX≤

……..

- kP contient les éléments kX>

Page 14: Les arbres généraux - UQAC

14

• Toutes les feuilles sont au même niveau.

Le sous arbre le plus à gauche contient toutes les valeurs inférieures à v1, le 2ème sous-arbre contient celles comprises entre v1 et v2, et ainsi de suite.

3.2 Opérations de base sur un B-arbre

3.2.1) Recherche d’un élément

La recherche dans un B-arbre est une généralisation de la recherche dans un arbre binaire de recherche Noter que l’on doit lire le nœud cherché en mémoire secondaire, puis faire une recherche (qui peut être dichotomique, pourquoi?) dans le nœud lui-même pour accéder à l’enregistrement recherché.

Page 15: Les arbres généraux - UQAC

15

Pseudo-code :

1. ki < x < ki+1 pour 0< i <m : x est compris entre les clés ki et ki+1, auquel cas on continue la recherche sur l’enfant pi

2. km < x : x est plus grande que toutes les clés du nœud, auquel cas on continue la recherche sur l,enfant pm

3. x < k1 : x est plus petite que toutes les clés du noeud, auquel cas on continue la recherche sur l’enfant indiquée par p0.

3.2.2 Insertion et suppression :

Les ajouts et suppressions d'éléments entraînent des opérations d'éclatement et de regroupement des sommets. Avant toute chose, il est nécessaire de retrouver (à l’aide de la fonction de recherche) la place où il faut insérer l’élément où l’élément en question.

a. Insertion

L’insertion dans un B-arbre est plus complexe que l’insertion dans un ABR. Elle utilise l’opération auxiliaire de découpage d’un nœud complet, que nous allons d’abord décrire. On rappelle qu’un nœud complet contient 2t clés. Après insertion, ce nombre devient impair. Le nœud possède donc une clé médiane. Le découpage consiste à éclater le nœud complet en deux nœuds, autour de cette clé médiane. La clé médiane (avec son enregistrement) remonte dans le nœud père, qui ne doit pas être complet avant le découpage. Chacun des deux nouveaux nœuds contient donc exactement t clés, le minimum requis. Si le nœud complet découpé était la racine (et n’a donc pas de père) une nouvelle racine va être créée pour recueillir la clé médiane, et la hauteur va augmenter de 1. C’est ainsi que croît le B-arbre: par la racine. La figure 11.2 illustre l’opération de découpage d’un nœud complet différent de la racine. La figure 11.3 illustre le cas du découpage de la racine. La figure 11.4 montre une succession d’insertions dans le même B-arbre. On montre que l’insertion dans un B-arbre nécessite O(h) = O(log n) accès à des nœuds. Remarque: certains auteurs définissent les bornes de chaque nœud, pour un arbre de degré t, comme étant t-1 et 2t-1 (comme c’est le cas ci-dessous)

Page 16: Les arbres généraux - UQAC

16

Page 17: Les arbres généraux - UQAC

17

Algorithme d’insertion 1. chercher le nœud où le nouvel élément doit être inséré 2. insérer l'élément dans son noeud 3. si débordement

partager le bloc en deux blocs à moitié pleins si (le noeud n'est pas la racine)

insérer de la même manière la valeur médiane dans le noeud de niveau supérieur où elle servira de critère de séparation (cette insertion peut elle-même créer une séparation dans les noeuds supérieurs)

sinon créer un nouveau noeud racine pour y mettre cette valeur médiane.

Page 18: Les arbres généraux - UQAC

18

b. Suppression d’un élément C’est une opération relativement complexe, que nous ne détaillerons pas ici. On peut deviner cependant que la plupart du temps elle nécessite uniquement la lecture des nœuds d’un chemin jusqu’à une feuille, parce que la plupart des clés se trouvent dans les feuilles. Si tel n’est pas le cas, on recherche l’élément le plus grand à la gauche de cet élément (où l’élément le plus petit à la droite de cet élément) qui ne peut se trouver que dans une feuille. On remplace l’élément à supprimer par cet élément qui lui doit être supprimé de ce nœud terminal Dans ce qui suit, nous allons donc supposer, sans perdre de généralité, que le nœud à supprimer est dans une feuille. La suppression dans un B-arbre s’effectue également en O(h) = O(log n) accès à des nœuds. Pour retrouver la clé proprement, une recherche s’impose dans le nœud lui-même.

Page 19: Les arbres généraux - UQAC

19

Algorithme de suppression 1. chercher le noeud s où se trouve l'élément à supprimer 2. enlever l'élément du noeud 3. si le noeud contient moins que m (l’ordre de cet arbre) éléments

si (nb. élt d'un noeud adjacent t + nb élt de s < m) - fusionner s, t et l'élément du noeud supérieur e qui les séparait en un noeud u - enlever e du noeud supérieur - remplacer les deux références à s et t par une seule référence à u - si (le noeud supérieur contient moins que m éléments)

appliquer récursivement le même traitement, sauf si c'est la racine

Page 20: Les arbres généraux - UQAC

20

sinon - effectuer une répartition égale des éléments entre s et un noeud adjacent t, - mettre à jour l'élément séparateur dans le noeud supérieur.

3.3. Complexité Comme nous venons de le voir, le coût des opérations d’insertion, suppression et recherche est proportionnel à la hauteur de l’arbre. Dans ce qui suit, nous montrons que la hauteur d’un B-arbre est en )(log nO m . En effet, nous savons que le nombre de clés, n, dans l’arbre vérifie la relation

suivante:

≥n minimum #nœuds du B-arbre de hauteur h et de degré minimum m

Le minimum #noeuds signifie que la racine à une seule clé (deux enfants) et les autres nœuds en ont m (minimum) clés chacun (comme illustré sur la figure ci-dessous).

Page 21: Les arbres généraux - UQAC

21

Dans ce cas, donc, nous avons une seul nœud d’une clé au niveau 1, 2 nœuds de m clés au 2ème niveau, )1(2 +m nœuds de m valeurs chacun au niveau 3 (c’est-à-dire )1(2 +mm valeurs), ….,

2)1(2 −+ hm nœuds de m clés chacun au niveau h (c’est-à-dire 2)1(2 −+ hmm Par conséquent, le nombre minimum de nœuds dans l’arbre est la somme des clés sur chaque niveau:

∑=

−++≥h

i

immn2

2)1(21

∑−

=

++≥2

1

)1(21h

i

immn

−++≥−

m

mmn

h 1)1(21

1

(série géométrique de raison (m+1));

1 clé

m clés m clés

m+1 branches

m clés

niveau 2

niveau 1

Page 22: Les arbres généraux - UQAC

22

en mettant le 1 du coté de n, en divisant par 2 et le log des deux cotés de cette inégalité, on obtient :

)1log()1(2

1log +−≥

+mh

n

)1(2

1log 1 −≥

++ h

nm

12

1log 1 +

+≤ +n

h m

)(log nOh m=

Il est facile de déduire la complexité des opérations de recherche, d’insertion et de suppression. Dans tous les cas, ces complexités sont proportionnelles à la hauteur de l’arbre. Une fois, le nœud trouvé, il y a lieu de parcourir la liste des valeur existant dans ce nœud. Comme les valeur dans chaque nœud sont triées, on pourra appliquer l’algorithme de dichotomie pour retrouver cette valeur en )(logmO (ou en O(m) si on applique la recherche séquentielle). Comme le coût de l’opération de recherche est dominé par la recherche du nœud (page) représentant les accès au disques, on dira que les complexité de ces algorithme sont en )(log nO m .

Pour l’opération d’insertion, dans le pire cas, après avoir trouvé l’endroit où il faut insérer l’élément, on remontera l’arbre, d’éclatement en éclatement de nœud jusqu’à créer éventuellement une nouvelle racine. Ce parcours n’excédera pas la hauteur de l’arbre + 1. Autrement dit, la complexité de l’insertion est en )(log nO m .

Pour la suppression, dans le pire cas, après avoir trouvé l’emplacement de l’élément à supprimer, on remontera l’arbre, de regroupement en regroupement de nœuds jusqu’à éventuellement la racine. Dans tous les cas, ce parcours n’excédera pas la hauteur de l’arbre. Autrement dit, la complexité de la suppression est en )(log nO m .

3.4. Variantes du B-arbre

1. B+-arbre: Dan un a B+ arbres, les données sont enregistrées dans les feuilles. Les nœuds internes contiennent juste des clés qui sont utilisées pour diriger la recherche vers la feuille considérée. Par ailleurs, les données sont chacune contenues dans une feuille et sont chaînées entre-elles de telle manière que ces dernières peuvent être toutes traversées. La suppression se trouve ainsi facilitée car elle se fait sur les feuilles.

2. B*-arbre: Similaire aux B-arbres, seulement les noeuds sont occupés au moins au 2/3.

3. Compact B-arbre: Similaire aux B-arbres, seulement les nœuds sont occupés à au

moins 90%.

4. Arbres 2-3 : Ce sont des B-arbres particuliers de degrés 1. Autrement dit, chaque nœud contient 1 à 2 clés et 2 ou 3 enfants.

Page 23: Les arbres généraux - UQAC

23

5. Arbre 2-3-4 : Pour éviter le cas de la dégénérescence de l'arbre, nous allons augmenter

les directions de recherche en autorisant les noeuds à devenir des 4 noeuds; c'est à dire un noeud possédant au maximum 4 noeuds enfants et donc 3 clés.

Les clés à l'intérieur d'un noeud sont toujours classées selon le principe des arbres binaires de recherche. Les noeuds enfants se soumettent à cette propriété aussi .