La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

Structures de données IFT-2000 Abder Alikacem Semaine 11 Gestion des arbres binaires de tri et de recherche. Les arbres cousus. Les arbres n-aires Département.

Présentations similaires


Présentation au sujet: "Structures de données IFT-2000 Abder Alikacem Semaine 11 Gestion des arbres binaires de tri et de recherche. Les arbres cousus. Les arbres n-aires Département."— Transcription de la présentation:

1

2 Structures de données IFT-2000 Abder Alikacem Semaine 11 Gestion des arbres binaires de tri et de recherche. Les arbres cousus. Les arbres n-aires Département dinformatique et de génie logiciel Édition Septembre 2009

3 Gestion des arbres binaires Algorithmes de gestion dun arbre binaire de tri Les arbres de recherche, arbres AVL Algorithme de balancement dun arbre AVL Les arbres cousus Les arbres n-aires

4 Implantation dun arbre binaire par chaînage template class Arbre { public: //.. private: // classe Noeud class Noeud { public: E data; Noeud *gauche; Noeud *droite; int card; int hauteur; Noeud( const E&d ): gauche(0),data( d ),droite(0),hauteur(0) { } }; // Les membres données Noeud * racine;//racine de l'arbre long cpt;// Nombre de noeuds dans l'arbre // Les membres fonctions privés //... };... data Modèle dimplantation par chaînage

5 Implantation dun arbre binaire par chaînage template class Arbre { public: //Constructeurs Arbre(){racine = 0; cpt=0;} Arbre(const Arbre& source) { _auxCopier(source.racine,racine);} //…//… //Destructeur ~Arbre() { _auxDetruire(racine);} //… //… //Les membres méthodes bool estVide(){ return cpt==0;} long taille() {return cpt;} int hauteur() throw(logic_error); //Par parcours//Par parcours E max() const throw(logic_error); // allons voir..// allons voir.. E min()const throw(logic_error); //…//… int nbFeuilles()const; //…//… int nbNoeuds()const; //Taille de l'arbre par parcours//Taille de l'arbre par parcours E parent(const E&) throw(logic_error); //..//.. E successeur(const E& ) throw(logic_error); //..//.. void lister(E*, int&) const; //…//… void lister(vector &) const; //…//… //.. Linterface publique

6 Implantation dun arbre binaire par chaînage template class Arbre { public: //.. bool appartient(const E &);//…//… void insererAVL(const E &data) throw(bad_alloc);//..//.. void enleverAVL( const E&) throw(logic_error);//…//… void parcourirPreOrdre(void (* traitement)(E &iteme)) const; //…//… void parcourirEnOrdre(void (* traitement)(E &iteme)) const; void parcourirPostOrdre(void (* traitement)(E &iteme)) const; void parcourirParNiveau(void (* traitement)(E &iteme)) const; //…//… //surcharge d'opérateurs void operator = (const Arbre & a) {…; _auxCopier(a.racine,racine);} bool operator == (const Arbre &a) { return( _auxArbresEgaux(racine,a.racine));} //… private: // classe Nœud //..

7 Implantation dun arbre binaire par chaînage template class Arbre { private: //..Les membres méthodes privés // Les auxiliaires récursifs pour l'insertion et les différents parcours, E _max(Noeud*)const throw (logic_error); E _min(Noeud*)const throw (logic_error); int _hauteurParcours(Noeud *); int _nbFeuilles(Noeud*) const; int _nbNoeuds(Noeud*) const; Noeud* _parent(Noeud*, Noeud*); E _successeur(Noeud*, const E&) throw(logic_error); void _auxEnOrdre(Noeud*, E*, int&) const; void _auxEnOrdre(Noeud*, vector &) const; bool _auxArbresEgaux (Noeud *, Noeud *);//… void _auxCopier( Noeud *, Noeud*&); void _auxDetruire(Noeud *&t); void _auxInserer( Noeud *&, const E&); void Arbre ::_auxInsererAVL(Noeud *&arbre, const E &data); //..

8 Implantation dun arbre binaire par chaînage template class Arbre { private: //..Les membres fonctions privés, suite… void _auxEnlever( Noeud* &, const E&)throw(logic_error); void _auxRetireMin( Noeud* &) const; Noeud* _auxAppartient(Noeud* & arbre, const E &data); void _auxPreOrdre(Noeud *,void (* traitement)(E&)) const; void _auxEnOrdre(Noeud *,void (* traitement)(E&)) const; void _auxPostOrdre(Noeud *,void (* traitement)(E&)) const; // Les membres privés propres aux arbres AVL void _zigZigGauche(Noeud *&); void _zigZigDroit(Noeud *&); void _zigZagGauche(Noeud *&); void _zigZagDroit(Noeud *&); int _hauteur(Noeud *); int _maximum(int,int); };

9 Ajout déléments dans un arbre de tri ,3,11,2,1,6,10,9,12,4,5,14

10 ,3,11,2,1,6,10,9,12,4,5,14 Ajout déléments dans un arbre de tri

11 ,3,11,2,1,6,10,9,12,4,5,14 Ajout déléments dans un arbre de tri

12 ,3,11,2,1,6,10,9,12,4,5,14 Ajout déléments dans un arbre de tri

13 ,3,11,2,1,6,10,9,12,4,5,14 Ajout déléments dans un arbre de tri

14 ,3,11,2,1,6,10,9,12,4,5,14 Ajout déléments dans un arbre de tri

15 ,3,11,2,1,6,10,9,12,4,5,14 Ajout déléments dans un arbre de tri

16 1 re séquence dinsertions ,3,11,2,1,6,10,9,12,4,5,14 O(log n)

17 2 e séquence dinsertions 1 2 1,2,3,4,5,6,8,9,10,11,12, O(n/2)

18 Analyse Le prix dune opération (recherche, insertion, retrait) est proportionnel au nombre de noeuds visités Donc, coût proportionnel à 1+hauteur de larbre (un coût par niveau) Meilleur cas: arbre équilibré (les feuilles à peu près toutes à la même profondeur) insertion et retrait aléatoire tendent à créer un arbre équilibré profondeur = O(log n) Pire cas: liste chaînée par exemple lors de linsertion déléments ordonnés profondeur = n Donc, le coût est O(log n) dans le meilleur cas et O(n) dans le pire cas

19 template void Arbre ::inserer(const E &data) throw(bad_alloc) { _auxInserer(racine, data); } template void Arbre ::_auxInserer(Noeud *&arbre, const E &data) { if (arbre == 0) { arbre = new Noeud(data); cpt++; } else if(arbre->data > data ) _auxInserer(arbre->gauche, data); else _auxInserer(arbre->droite, data); } Insertion sans balancement

20 Arbres équilibrés – concepts de base Situation idéale visée: sassurer que le sous-arbre de gauche et le sous- arbre de droite sont de même hauteur Ce principe sappliquerait à tous les noeuds de manière récursive Si on appliquait ceci à chaque insertion ou retrait, ce serait très coûteux Il faut donc établir des conditions plus faibles, mais qui nous assurent des gains en performance, nous verrons cela lorsquon parlera des arbres rouge et noir. Arbre bien équilibréArbre mal équilibré

21 Arbres binaires équilibrés (AVL) Rappel Cest un arbre de recherche binaire tel que pour chaque noeud, les hauteurs des ses sous-arbres gauche et droite sont différentes dau plus k, k étant le critère déquilibre (on attribue comme hauteur la valeur -1 pour un sous-arbre vide). K =1 dans le cas des arbres AVL. Avec cette condition, on est assuré de toujours avoir un arbre dont la profondeur est proportionnelle à log (n). arbres AVL = HB[1] (arbres Adelson-Velski et Landis) arbres HB[k] « Un arbre T est HB[k] si T et tous ses sous-arbres ont la propriété HB[k] qui est : les sous-arbres gauche et droit diffèrent en hauteur dau plus k. »

22 Arbres équilibrés (AVL) Bien équilibré selon la règle AVL Mal équilibré selon la règle AVL 1

23 Arbres équilibrés (AVL) Il faut, après chaque insertion ou retrait, rétablir léquilibre sil a été rompu par lopération. Observation importante: après une insertion, seuls les noeuds qui sont sur le chemin du point dinsertion à la racine sont susceptibles dêtre déséquilibrés. Deux cas: insertion dans le sous-arbre de gauche du fils gauche ou dans le sous- arbre de droite du fils droit: Simple rotation insertion dans le sous-arbre de droite du fils gauche ou dans le sous- arbre de gauche du fils droit: Double rotation On doit distinguer 4 cas en tout, deux cas à gauche et deux cas à droite.

24 Équilibration : HB[1] 2 cas (~gauche) S1 A B S2 S3 h + 1 h + 2 h + 3 h + 4 N S1 A B S2 S3 h + 1 h + 2 h + 3 h + 4 N

25 Rotation simple S1 A B 2 S3 h + 1 h + 2 h + 3 h + 4 N S1 A B 2 S3 h + 1 h + 2 h + 3 h + 4 N avantaprès S2

26 Rotation simple S1 A B 2 S3 h + 1 h + 2 h + 3 h + 4 N S1 A B 2 S3 h + 1 h + 2 h + 3 h + 4 N avantaprès S2

27 Rotation simple S1 A B 2 S3 h + 1 h + 2 h + 3 h + 4 N S1 A B 2 S3 h + 1 h + 2 h + 3 h + 4 N avantaprès S2

28 S1 A B 2 S3 h + 1 h + 2 h + 3 h + 4 N Rotation simple hauteur initiale vs hauteur finale S1 A B S2 S3 h + 1 h + 2 h + 3 h + 4 N avantaprès S2

29 Rotation double S1 A B S2 S3 h + 1 h + 2 h + 3 h + 4 N S1 A B S2.1 S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C

30 Rotation double S1 A B S2.1 S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C S1 A B S2.1 S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C avantaprès

31 Rotation double S1 A B S2.1 S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C S1 B S2.1 S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C A avantaprès

32 Rotation double S1 B S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C S1 B S3 h + 1 h + 2 h + 3 h + 4 N C A A S2.1 S2.2 avantaprès

33 Rotation double S1 B S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C S1 A B S2.1 S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C hauteur initiale vs hauteur finale A S2.1 avantaprès

34 1 re rotation B S3 h + 1 h + 2 h + 3 h + 4 N S2.2 S1 A B S2.1 S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C S2.1 S1 C A avantaprès

35 2 e rotation B S3 h + 1 h + 2 h + 3 h + 4 N S2.2 S2.1 S1 C A S1 B S3 h + 1 h + 2 h + 3 h + 4 N S2.2 C A S2.1 avantaprès

36 Nœud critique Mais ce nest pas toujours le cas Ici, le nœud critique du déséquilibre est la racine 1 Quand il y a un déséquilibre, le nœud le plus bas à partir duquel il y a une différence de 2 ou plus est appelé le nœud critique.

37 Nœud critique Il peut parfois y avoir plusieurs nœuds débalancés. Dans ce cas, on soccupe dabord du plus bas de tous, cest le nœud critique.

38 Maintien de léquilibre Quand on implémente un arbre AVL, il faut maintenir son équilibre. Les déséquilibres surviennent soit lors dun ajout, soit lors dune suppression. Le ou les nœuds critiques engendrés, sil y a lieu, sont toujours sur le chemin de lajout ou de la suppression. Nouveau noeud

39 Rééquilibrer un arbre déséquilibré Quand un déséquilibre apparaît, il faut remodeler la partie de larbre dont la racine est le nœud critique. Nouveau noeud

40 Les quatre cas de déséquilibre Il faut dabord identifier le genre de déséquilibre auquel on a affaire. Dabord, il faut voir de quel côté larbre penche à partir du nœud critique. Dans le cas de lexemple ci-bas, cest vers la gauche. Nouveau noeud

41 Les quatre cas de déséquilibre Lenfant immédiat du nœud critique du côté vers lequel larbre penche sappelle le nœud sous-critique. Nœud critique Nœud critique Nœud sous-critique Nœud sous-critique Nouveau noeud

42 Les quatre cas de déséquilibre Puis il faut regarder de quel côté penche larbre à partir du nœud sous- critique. Larbre sous-critique na pas besoin dêtre déséquilibré pour quon considère quil penche. Une différence de 1 suffit pour identifier le cas (contrairement à la vérification quon faisait au départ pour vérifier sil y avait déséquilibre). Dans lexemple ci-bas, larbre sous-critique penche vers la droite. Nœud critique Nœud critique Nœud sous-critique Nœud sous-critique Nouveau noeud

43 Les quatre cas de déséquilibre Nous avons donc, dans cet exemple-ci, un déséquilibre vers la gauche, avec un arbre sous-critique qui penche vers la droite. Quand le sens du déséquilibre principal est différent du sens dans lequel larbre sous-critique penche, alors deux rotations sont nécessaires. Nous appelons ça un zig-zag. Quand le sens du déséquilibre principal est le même que le sens dans lequel larbre sous-critique penche, ou bien que larbre sous-critique ne penche pas dutout, alors une seule rotation est nécessaire, et il sagit dun zig-zig. Dans notre exemple, nous aurons donc à faire deux rotations. Nœud critique Nœud critique Nœud sous-critique Nœud sous-critique Nouveau noeud

44 Les rotations Quand nous nous retrouvons dans le cas où il faut faire deux rotations, la première sert finalement à faire pencher larbre sous-critique dans le même sens que larbre critique, de façon à nous retrouver dans le cas simple dune seule rotation. Nœud critique Nœud critique Nœud sous-critique Nœud sous-critique Nouveau noeud

45 Les rotations Nœud critique Nœud critique Nœud sous-critique Nœud sous-critique Nouveau noeud Première rotation (préparatoire à la deuxième).

46 Les rotations Nœud critique Nœud critique Nouveau noeud Première rotation (préparatoire à la deuxième).

47 Les rotations Première rotation (préparatoire à la deuxième). Nœud critique Nœud critique Nouveau noeud

48 Les rotations Première rotation (préparatoire à la deuxième). Nœud critique Nœud critique Nouveau noeud

49 Les rotations Première rotation (préparatoire à la deuxième). Nœud critique Nœud critique Nouveau noeud

50 Les rotations Première rotation (préparatoire à la deuxième). Nœud critique Nœud critique Nouveau noeud

51 Les rotations Première rotation (préparatoire à la deuxième). Nœud critique Nœud critique Ancien nœud sous-critique Nouveau noeud Nœud nouvellement sous-critique Nœud nouvellement sous-critique

52 Les rotations Deuxième rotation. Nœud critique Nœud critique Nouveau noeud

53 Les rotations Deuxième rotation. Nouveau noeud

54 Les rotations Deuxième rotation. Nouveau noeud

55 Les rotations Deuxième rotation. Nouveau noeud

56 Les rotations Deuxième rotation. Nouveau noeud

57 Les rotations Deuxième rotation. Nouveau noeud

58 Les rotations Deuxième rotation. Nouveau noeud

59 Les rotations Les deux rotations sont terminées, nous avons maintenant un arbre AVL équilibré. Nouveau noeud

60 Implémentation : remarques indice de débalancement : tag = hauteur(gauche) - hauteur(droit) valeurs : 2, 1, 0, -1, -2 calcul de hauteur(gauche) - hauteur(droit) au parcours dinsertion + stockage de la hauteur mise à jour un seul rebalancement requis la racine de larbre rebalancé a changé

61 Modèle dimplantation arbre AVL template class Arbre { public: //.. private: // classe Noeud class Noeud { public: E data; Noeud *gauche; Noeud *droite; int card; int hauteur; Noeud( const E&d ): gauche(0),data( d ),droite(0),hauteur(0) { } }; // Les membres données Noeud * racine;//racine de l'arbre //... };... data Modèle dimplantation par chaînage

62 Implémentation de linsertion dans un arbre AVL Algorithme récursif Une fois le noeud inséré, en revenant sur notre chemin, il faut vérifier, pour chaque noeud parcouru, les différences de profondeur des sous- arbres gauche et droite. La rotation peut être requise à nimporte quel noeud qui se trouve dans le chemin de la racine au point dinsertion.

63 template int Arbre :: _hauteur(Noeud *arb) { if (arb == 0) return -1; return arb->hauteur; } template int Arbre :: _maximum(int ent1, int ent2) { if (ent1 <= ent2) return ent2; else return ent1; } deux méthodes utiles.. Insertion dans un arbre AVL

64 Algorithme insereAvl (Nœud* & T, int x) Début Si T = NULL, alors Début Allocation de mémoire à l'adresse T T.element = x T. filsG = NULL et T. filsD = NULL T.hauteur = 0 Fin Sinon Début Si x < T.element, alors Appel insereAvl(T.filsG, x) Si Hauteur (T.filsG)-Hauteur (T.filsD) = 2 alors Si x < T. filsG.element, alors Appel ZigZigGauche (T) Sinon Appel ZigZagGauche (T) Sinon T.hauteur =Max(Hauteur (T.filsG),Hauteur (T.filsD))+1 Sinon Début /* Cas symétrique pour le sous-arbre droit */ Fin

65 algorithme ZigZigGauche (Nœud* &K2) Début K1 = K2.filsG K2.filsG = K1.filsD K1.filsD = K2 K2.hauteur = Max (Hauteur (K2.filsG), Hauteur (K2.filsD)) +1 K1.hauteur = Max (Hauteur (K1.filsG), K2.hauteur) +1 K2 = K1 Fin + k2 k1 k2 k1

66 algorithme ZigZigDroit (Nœud* &K2) Début K1 = K2.filsD K2.filsD = K1.filsG K1.filsG = K2 K2.hauteur = Max (Hauteur (K2.filsD), Hauteur (K2.filsG)) +1 K1.hauteur = Max (Hauteur (K1.filsD), K2.hauteur) +1 K2 = K1 Fin simple rotation, déséquilibre vers la gauche k2 k1

67 algorithme ZigZagGauche (Nœud * &K3) Début ZigZigDroit (K3.filsG) ZigZigGauche (K3) Fin + k3

68 algorithme ZigZagDroit (Nœud * &K3) Début ZigZigGauche (K3.filsD) ZigZigDroit (K3) Fin + k3

69 template void Arbre :: _zigZigDroit(Noeud * &K2) { Noeud *K1; K1 = K2->droite; K2->droite = K1->gauche; K1->gauche = K2; K2->hauteur = 1 + _maximum(_hauteur(K2->droite), _hauteur(K2->gauche)); K1->hauteur = 1 + _maximum(_hauteur(K1->droite), K2->hauteur); K2 = K1; } Implémentation dune rotation

70 AVL – exemple détaillé Pour chaque noeud on mettra 0 si ses deux sous-arbres ont la même hauteur +1 si le sous-arbre gauche est plus profond avec une différence = 1 -1 si le sous-arbre droit est plus profond avec une différence = 1 Séquence dinsertion:

71 AVL – exemple détaillé

72 AVL – exemple détaillé

73 AVL – exemple détaillé Nœud critique

74 AVL – exemple détaillé Rotation simple 0 0 0

75 AVL – exemple détaillé

76 AVL – exemple détaillé

77 AVL – exemple détaillé Nœud critique

78 AVL – exemple détaillé Rotation simple

79 AVL – exemple détaillé

80 AVL – exemple détaillé Nœud critique

81 AVL – exemple détaillé Rotation double

82 AVL – exemple détaillé Voici un exemple où la rotation se fait loin du point dinsertion Noeud inséré Nœud critique

83 AVL – exemple détaillé Voici un exemple où la rotation se fait loin du point dinsertion Après rotation double 0 0 0

84 Analyse insertion balancée : trouver le point dinsertion : O(log n) insertion dune feuille : O(1) + vérification et rebalancement: on remonte (suite aux appels récursifs) : O(log n) on vérifie le rebalancement possible : O(1) on rebalance au besoin : O(1) total : O(log n)

85 Enlèvement dans un arbre AVL Pour supprimer un nœud dans un arbre AVL, il y a deux cas simples et un cas compliqué: Premier cas simple: le nœud à supprimer est une feuille. Dans ce cas, il suffit de le supprimer directement. Deuxième cas simple: le nœud à supprimer possède un seul enfant. Dans ce cas, il suffit de le supprimer et de le remplacer par son seul enfant. Cas compliqué: le nœud à supprimer a deux enfants. Dans ce cas, il faut dabord échanger ce nœud avec son successeur, puis le supprimer à son nouvel endroit, ce qui nous mènera nécessairement à lun des deux cas simples. Bien entendu, il faut aussi vérifier les déséquilibres en remontant jusquà la racine. Lalgorithme fonctionne aussi bien si on prend le prédécesseur plutôt que le successeur. Étant donné que la nécessité de retrouver le successeur ne survient que dans le cas où le nœud a deux enfants, alors nous sommes nécessairement toujours en présence du cas simple de recherche du successeur! Ainsi, une simple boucle suffit.

86 Enlèvement dans un arbre AVL une feuille : trivial 48 Analyse : l algorithme de suppression d un nœud présente donc 3 cas : O 45 gauche 48 droit O 45 29

87 Enlèvement dans un arbre AVL un nœud simple : on le remplace par son unique fils 50 Deuxième cas de nœud à supprimer O 45 gauche 48 droit

88 Enlèvement dans un arbre AVL 23 un nœud double : on lui donne la valeur minimale de son sous-arbre droit (ex: 29), et on supprime le nœud qui a cette valeur Troisième cas de nœud à supprimer O 45 gauche 48 droit O

89 88 Exemple denlèvement AVL Supprimons le nœud 36 de cet arbre-ci. Il faut dabord le repérer avec une recherche conventionnelle à partir de la racine

90 89 Puis, nous établissons quil sagit dun cas compliqué car le nœud à supprimer a deux enfants Exemple denlèvement AVL

91 90 Il faut donc dabord retrouver son successeur à laide dune boucle simple (une fois à droite, plein de fois à gauche) Exemple denlèvement AVL

92 91 Puis on léchange avec Exemple denlèvement AVL

93 92 Puis on léchange avec Exemple denlèvement AVL

94 93 Puis on léchange avec Exemple denlèvement AVL

95 94 Puis on léchange avec Exemple denlèvement AVL

96 95 Puis on léchange avec Exemple denlèvement AVL

97 96 Puis on léchange avec Exemple denlèvement AVL

98 97 Puis on léchange avec Exemple denlèvement AVL

99 98 Puis on léchange avec Exemple denlèvement AVL

100 99 Puis on léchange avec Exemple denlèvement AVL

101 100 Puis on léchange avec Exemple denlèvement AVL

102 101 Puis on léchange avec Exemple denlèvement AVL

103 102 Puis on léchange avec Exemple denlèvement AVL

104 103 Puis on léchange avec Exemple denlèvement AVL

105 104 Puis on léchange avec Exemple denlèvement AVL

106 105 Puis on léchange avec Exemple denlèvement AVL

107 106 Puis on léchange avec Exemple denlèvement AVL

108 107 Puis on léchange avec Exemple denlèvement AVL

109 108 Remarquez que la règle dordonnancement darbre binaire de recherche est temporairement enfreinte Exemple denlèvement AVL

110 109 Ensuite, on continue à descendre récursivement pour supprimer 36, comme si rien nétait Exemple denlèvement AVL

111 110 Puis lorsquon retombe sur 36, on arrive nécessairement à lun des deux cas simple Exemple denlèvement AVL

112 111 Dans ce cas-ci, il sagit du cas avec un seul enfant. 36 sera donc remplacé par Exemple denlèvement AVL

113 112 Dans ce cas-ci, il sagit du cas avec un seul enfant. 36 sera donc remplacé par Exemple denlèvement AVL

114 113 Dans ce cas-ci, il sagit du cas avec un seul enfant. 36 sera donc remplacé par Exemple denlèvement AVL

115 114 Dans ce cas-ci, il sagit du cas avec un seul enfant. 36 sera donc remplacé par Exemple denlèvement AVL

116 115 Dans ce cas-ci, il sagit du cas avec un seul enfant. 36 sera donc remplacé par Exemple denlèvement AVL

117 116 Dans ce cas-ci, il sagit du cas avec un seul enfant. 36 sera donc remplacé par Exemple denlèvement AVL

118 117 Puis le nœud 36 est détruit avec « delete ». Ensuite, il faut remonter jusquà la racine pour vérifier léquilibre de larbre Exemple denlèvement AVL

119 118 Puis le nœud 36 est détruit avec « delete ». Ensuite, il faut remonter jusquà la racine pour vérifier léquilibre de larbre Exemple denlèvement AVL

120 119 Puis le nœud 36 est détruit avec « delete». Ensuite, il faut remonter jusquà la racine pour vérifier léquilibre de larbre Exemple denlèvement AVL

121 120 Puis le nœud 36 est détruit avec « delete ». Ensuite, il faut remonter jusquà la racine pour vérifier léquilibre de larbre Exemple denlèvement AVL

122 121 Puis le nœud 36 est détruit avec « delete ». Ensuite, il faut remonter jusquà la racine pour vérifier léquilibre de larbre Exemple denlèvement AVL

123 122 template void Arbre ::enlever(const E& data) throw(logic_error) { if( racine == 0 ) throw logic_error("Enlever: l'arbre est vide\n"); if( _auxAppartient(racine, data) == 0 ) throw logic_error("Enlever: l'element nest pas dans l'arbre\n"); _auxEnlever(racine, data); //data est certain dans l'arbre } Enl è vement dans un arbre de tri Enlèvement sans balancement

124 123 template void Arbre :: _auxEnlever(Noeud * & t, const E & valeur) throw(logic_error) { if( t->data > valeur) _auxEnlever( t->gauche, valeur); else if( t->data < valeur ) _auxEnlever( t->droite, valeur); else if( t->gauche != 0 && t->droite != 0 ) {//Troisième cas //chercher le noeud qui contient la valeur minimale dans le sous-arbre droit Noeud * temp = t->droite; while ( temp->gauche != 0) temp = temp->gauche; t->data = temp->data; _auxRetireMin( t->droite ); // Retirer minimum dans le sous-arbre droit } else { //Premier ou deuxième cas // le noeud n'a aucun enfant ou qu'un seul enfant, il suffit donc de retirer // ce noeud et pointer sur l'éventuel enfant Noeud * vieuxNoeud = t; t = ( t->gauche != 0 ) ? t->gauche : t->droite; delete vieuxNoeud; }

125 124 template void Arbre :: _auxRetireMin( Noeud* & t) const throw(logic_error) { if (t == 0) throw logic_error("_auxRetireMin: pointeur NULL\n"); else if (t->gauche != 0) _auxRetireMin( t->gauche ); else { Noeud * tmp = t; t = t->droite; delete tmp; }

126 Analyse enlèvement balancé : plusieurs rebalancements possiblement requis la racine de tout arbre rebalancé a changé trouver le nœud à enlever (déjà vu) : O(log n) + vérification et rebalancement: on remonte (suite aux appels récursifs) : O(log n) on vérifie le rebalancement possible : O(1) on rebalance au besoin : O(1) total : O(log n) (mais plus coûteux que linsertion)

127 template E Arbre ::max()const throw (logic_error) { if (cpt==0) throw logic_error("Max: l'arbre est vide!\n"); if (racine->droite == 0) { return racine->data; } Noeud * temp = racine->droite; while (temp->droite!=0) temp = temp->droite; return temp->data; }

128 template E Arbre ::_max(Noeud*racine)const throw (logic_error) { if (cpt==0) throw logic_error("Max: l'arbre est vide!\n"); if (racine->droite == 0) { return racine->data; } return _max(racine->droite); }

129 template E Arbre ::min()const throw (logic_error) { if (cpt==0) throw logic_error("Min: l'arbre est vide!\n"); if (racine->gauche == 0) { return racine->data; } Noeud * temp = racine->gauche; while (temp->gauche!=0) temp = temp->gauche; return temp->data; }

130 template E Arbre ::_min(Noeud*racine) const throw (logic_error) { if (racine==0) throw logic_error("Min: l'arbre est vide!\n"); if (racine->gauche == 0) { return racine->data; } return _min(racine->gauche); }

131 template int Arbre :: nbNoeuds() const { return _nbNoeuds(racine); } template int Arbre :: _nbNoeuds(Noeud* arb) const { if (arb==0) return 0; return _nbNoeuds(arb->gauche) + _nbNoeuds(arb->droite) + 1; }

132 template int Arbre ::nbFeuilles() const { return _nbFeuilles(racine); } template int Arbre ::_nbFeuilles(Noeud*arb) const { int nbG (0), nbD(0); if (arb != 0) { if (arb->gauche == 0 && arb->droite == 0) return 1; else { if (arb->gauche != 0) nbG = _nbFeuilles(arb->gauche); if (arb->droite != 0) nbD = _nbFeuilles(arb->droite); } return nbG + nbD; }

133 template int Arbre ::hauteur() throw (logic_error) { if (cpt==0) throw logic_error("Hauteur: l'arbre est vide!\n"); return _hauteurParcours(racine); } template int Arbre ::_hauteurParcours(Noeud * arb) { if (arb==0) return -1; return 1 + _maximum(_hauteur(arb->gauche), _hauteur(arb->droite)); }

134 template bool Arbre :: appartient(const E &data) { return _auxAppartient(racine, data)!=0; } template typename Arbre :: Noeud* Arbre :: _auxAppartient(Noeud* & arbre, const E &data) { if (arbre == 0) return 0; if ( arbre->data == data ) return arbre; if ( arbre->data > data ) return _auxAppartient(arbre->gauche, data); else return _auxAppartient(arbre->droite, data); }

135 template E Arbre :: parent(const E& el) throw(logic_error) { Noeud* noeudDeEl = _auxAppartient(racine, el); Noeud* parentDeEl = _parent(racine, noeudDeEl); return parentDeEl->data; }

136 template typename Arbre :: Noeud* Arbre :: _parent(Noeud* arb, Noeud* sArb) throw(logic_error) { if (arb == 0) throw logic_error("Parent: l'arbre est vide!\n"); if (sArb == 0) throw logic_error("Parent: l'element n'existe pas!\n"); if (sArb == arb) throw logic_error("Parent: Le parent de la racine d'existe pas!\n"); if ( sArb->data data ) { if (arb->gauche == sArb)return arb; elsereturn _parent(arb->gauche, sArb); } else { if (arb->droite == sArb)return arb; elsereturn _parent(arb->droite, sArb); }

137 template E Arbre :: successeur(const E& info) throw(logic_error) { return _successeur(racine, info); } template E Arbre :: _successeur(Noeud* arb, const E& info) throw (logic_error) { if (cpt == 0) throw logic_error("Successeur: l'arbre est vide!\n"); Noeud* sArb = _auxAppartient(racine, info); if (sArb == 0) throw logic_error("Successeur: l'element n'existe pas!\n"); if ( info == _max(arb)) throw logic_error("Successeur: l'element est le max dans l'arbre!\n"); if (sArb->droite != 0) return _min(sArb->droite); else { Noeud * pere = _parent(arb, sArb); while (pere->data data ) pere = _parent(arb,pere); return pere->data; }

138 template void Arbre :: lister(E* res, int&ind) const { _auxEnOrdre(racine, res, ind); } template void Arbre :: _auxEnOrdre(Noeud* arb, E* res, int&ind) const { if (arb == 0) return; else { _auxEnOrdre(arb->gauche, res, ind); /* traitement(arb) */ res[ind] = arb->data; ind++; _auxEnOrdre(arb->droite, res, ind); }

139 template void Arbre :: lister(vector & v) const throw(bad_alloc) { _auxEnOrdre(racine, v); } template void Arbre :: _auxEnOrdre(Noeud* arb, vector & v) const { if (arb == 0) return; else { _auxEnOrdre(arb->gauche, v); /* traitement(arb) */ v.push_back(arb->data); _auxEnOrdre(arb->droite, v); }

140 template void Arbre ::parcourirPreOrdre(void (* traitement)(E &iteme)) const { _auxPreOrdre(racine,traitement); } template void Arbre ::_auxPreOrdre(Noeud*arbre,void(*traitement)(E &iteme))const { if (arbre !=0) { traitement(arbre->data); _auxPreOrdre(arbre->gauche, traitement); _auxPreOrdre(arbre->droite, traitement); } template void imprime(T &x) { cout << x << " " ; } int main() { try { Arbre ab; ab.insererAVL(5); … ab.parcourirPreOrdre(imprime);

141 template void Arbre ::parcourirParNiveau(void (* traitement)(E &iteme)) const { queue Q; Noeud * temp; Q.push(racine); while (!Q.empty()) { temp= Q.front(); traitement(temp->data); Q.pop(); if(temp->gauche!=0)Q.push(temp->gauche); if(temp->droite!=0)Q.push(temp->droite); }

142 template void Arbre ::_auxCopier( Noeud * source, Noeud * & destination) throw(bad_alloc) { if (source!=0) { destination = new Noeud(source->data); destination->hauteur = source->hauteur; _auxCopier(source->gauche, destination->gauche); _auxCopier(source->droite, destination->droite); } else destination=0; }

143 template void Arbre ::_auxDetruire(Noeud *t) { if (t != 0) { _auxDetruire(t->gauche); _auxDetruire(t->droite); delete t; t= 0; }

144 template bool Arbre ::_auxArbresEgaux (Noeud * premier, Noeud * second) { if(( premier==0) && (second==0)) return true; else if ((premier!=0) &&(second!=0)) return((premier->data==second->data) && _auxArbresEgaux(premier->gauche,second->gauche) && _auxArbresEgaux(premier->droite,second->droite)); else return false; }

145 problèmes : remonter vers les parents : fonction parent à partir dun nœud retour par les appels récursifs espace à gérer : accès par pile : quelle est la taille de la pile ? combien de piles a-t-on besoin ? pour de nombreux usagers = trop despace ! Parcours avec pile

146 Peut-on éliminer les piles ? ajouter un pointeur vers le parent problèmes : ne jamais perdre le parent lors de lajout beaucoup despace perdu complique les rebalancements Parcours avec pile

147 Arbres cousus Peut-on éliminer les piles ? ajouter un pointeur vers le parent ajouter un pointeur vers le succ./préd. problèmes : - complique les ajouts - complique les rebalancements - il faut éviter les cycles

148 Arbres cousus class Noeud { public: E data; /* élément */ Noeud *gauche; /* ptr sur SAG ou prédécesseur */ Noeud *droite; /* ptr sur SAD ou successeur */ bool filGauche;/* indique si gauche est un fil ou non */ bool filDroit;/* indique si droite est un fil ou non */ //… }; Comment reconnaître les cycles ? utiliser un «tag» avec chaque pointeur

149 Arbres n-aires (pour n fixe) critère de branchement multiple exemples ? arbres-B analyse lexicale : 1 re lettre, 2 e lettre, etc. …

150 Arbres n-aires (pour n fixe) template class Arbre { public: //.. private: // classe Noeud class Noeud { public: E data; Noeud ** fils; int card; Noeud(const E&d ) { …} }; // Les membres données Noeud * racine;//racine de l'arbre //... };... data

151 Arbres n-aires (pour n variable) nombre de branchements inconnu au départ nombre de branchements très variable exemples ? structure organisationnelle analyse lexicale autres ?

152 Arbres n-aires variables modèles dimplantation ? tableau dynamique ou liste de pointeurs

153 Arbres n-aires variables modèles dimplantation ? vector ou liste de pointeurs

154 Arbres n-aires variables template class Arbre { public: //.. private: // classe Noeud class Noeud { public: E data; vector fils; int card; Noeud(const E&d ) { …} }; // Les membres données Noeud * racine;//racine de l'arbre //... };... data

155 Arbres n-aires variables modèles dimplantation ? tableau dynamique ou liste de pointeurs 2 types de pointeurs : 1 er fils, frère cadet

156 Arbres n-aires variables modèles dimplantation ? tableau dynamique ou liste de pointeurs 2 types de pointeurs : 1 er fils, frère cadet

157 Arbres n-aires variables modèles dimplantation ? tableau dynamique ou liste de pointeurs 2 types de pointeurs : 1 er fils, frère cadet

158 Arbres n-aires variables modèles dimplantation ? tableau dynamique ou liste de pointeurs 2 types de pointeurs : 1er fils, frère cadet


Télécharger ppt "Structures de données IFT-2000 Abder Alikacem Semaine 11 Gestion des arbres binaires de tri et de recherche. Les arbres cousus. Les arbres n-aires Département."

Présentations similaires


Annonces Google