TP 8 : Arbres binaires de recherche
Transcription
TP 8 : Arbres binaires de recherche
TP 8 : Arbres binaires de recherche Semaine du 17 Mars 2008 Exercice 1 struct typedef Dénir une structure noeud_s permettant de coder un n÷ud d'un arbre binaire contenant une valeur entière. Ajouter des pour dénir les nouveaux types noeud_t et arbre_t (ces types devraient permettre de représenter une feuille, c'est à dire un arbre vide). Correction typedef struct noeud_s { int v a l e u r ; struct noeud_s ∗ gauche ; struct noeud_s ∗ d r o i t ; I } ∗ noeud_t ; typedef noeud_t arbre_t ; Exercice 2 Écrire une fonction cree_arbre() qui prend en argument une valeur entière ainsi que deux arbres et renvoie un arbre dont la racine contient cette valeur et les deux sous-arbres sont ceux donnés en paramètre. Correction #include < s t d l i b . h> I int sizeof struct arbre_t c r e e _ a r b r e ( v a l e u r , arbre_t gauche , arbre_t d r o i t ) { arbre_t a r b r e = m a l l o c ( ( noeud_s ) ) ; a r b r e −>v a l e u r = v a l e u r ; a r b r e −>gauche = gauche ; a r b r e −>d r o i t = d r o i t ; arbre ; } return Exercice 3 Écrire une fonction (récursive) detruit_arbre() qui libère la mémoire occupée par tous les n÷uds d'un arbre binaire. I Correction #include < s t d l i b . h> void d e t r u i t _ a r b r e ( arbre_t a r b r e ) { if ( a r b r e == NULL) return ; } d e t r u i t _ a r b r e ( a r b r e −>gauche ) ; d e t r u i t _ a r b r e ( a r b r e −>d r o i t ) ; f r e e ( arbre ) ; Exercice 4 Écrire une fonction (récursive) nombre_de_noeuds() qui calcule le nombre de n÷uds d'un arbre binaire. 1 Correction int nombre_de_noeuds ( arbre_t a r b r e ) { if ( a r b r e == NULL) return 0 ; return ( 1 + nombre_de_noeuds ( a r b r e −>gauche ) I + nombre_de_noeuds ( a r b r e −>d r o i t ) ) ; } Exercice 5 Écrire une fonction ache_arbre() qui ache les valeurs des n÷uds d'un ABR par ordre croissant (choisissez le bon type de parcours des n÷uds de l'arbre. . . ). Correction #include <s t d i o . h> void a f f i c h e _ a r b r e _ r e c ( arbre_t a r b r e ) { if ( a r b r e != NULL) { a f f i c h e _ a r b r e _ r e c ( a r b r e −>gauche ) ; if ( a r b r e −>gauche != NULL) I printf (" ," ); p r i n t f ( "%d" , a r b r e −>v a l e u r ) ; ( a r b r e −>d r o i t != NULL) printf (" ," ); a f f i c h e _ a r b r e _ r e c ( a r b r e −>d r o i t ) ; if } } void } a f f i c h e _ a r b r e ( arbre_t a r b r e ) { affiche_arbre_rec ( arbre ) ; p r i n t f ( " \n" ) ; Exercice 6 Écrire une fonction ache_arbre2() permettant d'acher les valeurs des n÷uds d'un arbre binaire de manière à lire la structure de l'arbre. Un n÷ud sera aché ainsi : {g,v,d} où g est le sous-arbre gauche, v la valeur du n÷ud et d le sous-arbre droit. Par exemple, l'arbre de la gure 1 sera aché par : {{{_,1,_},3,_},4,{{_,6,_},6,{{_,7,_},9,_}}}. Les '_' indiquent les sous-arbres vides. Correction #include <s t d i o . h> void a f f i c h e _ a r b r e 2 _ r e c ( arbre_t if ( a r b r e == NULL) p r i n t f ( "_" ) ; else { I } void } arbre ) { p r i n t f ( "{" ) ; a f f i c h e _ a r b r e 2 _ r e c ( a r b r e −>gauche ) ; p r i n t f ( ",%d , " , a r b r e −>v a l e u r ) ; a f f i c h e _ a r b r e 2 _ r e c ( a r b r e −>d r o i t ) ; p r i n t f ( "}" ) ; a f f i c h e _ a r b r e 2 ( arbre_t a r b r e ) { 2 } affiche_arbre2_rec ( arbre ) ; p r i n t f ( " \n" ) ; Exercice 7 Écrire une fonction compare() qui compare deux arbres binaires (la fonction renvoie une valeur nulle si et seulement si les deux arbres binaires ont la même structure d'arbre et qu'ils portent les mêmes valeurs aux n÷uds se correspondant). Correction int compare ( arbre_t a r b r e 1 , arbre_t if ( a r b r e 1 == NULL) return ( a r b r e 2 != NULL ) ; else { ∗ ∗ if ( a r b r e 2 == NULL) return 1 ; else ∗ ∗ ∗ return ( ( a r b r e 1 −>v a l e u r I / arbre1 / / } arbre2 on } != NULL arbre2 ) { / != NULL / p a r e s s e u s e du | | ∗ / != a r b r e 2 −>v a l e u r ) | | compare ( a r b r e 1 −>gauche , a r b r e 2 −>gauche ) | | compare ( a r b r e 1 −>d r o i t , a r b r e 2 −>d r o i t ) ) ; utilise l ' évaluation Exercice 8 Écrire une fonction (récursive. . . ) insere () qui ajoute une valeur dans l' sera un nouveau n÷ud placé correctement dans l'arbre). I Correction arbre_t i n s e r e ( arbre_t a r b r e , int v a l e u r ) { if ( a r b r e == NULL) return c r e e _ a r b r e ( v a l e u r , NULL, NULL ) ; else { if ( v a l e u r < a r b r e −>v a l e u r ) a r b r e −>gauche = i n s e r e ( a r b r e −>gauche , v a l e u r ) ; else ∗ − ∗ a r b r e −>d r o i t = i n s e r e ( a r b r e −>d r o i t , v a l e u r ) ; return a r b r e ; / } v a l e u r >= arbre >v a l e u r ABR (ce / } Exercice 9 Écrire une fonction trouve_noeud() qui renvoie l'adresse d'un n÷ud de l'ABR donné en paramètre contenant une certaine valeur (ou NULL si cette valeur ne gure pas dans l'arbre). Correction noeud_t trouve_noeud ( arbre_t a r b r e , int v a l e u r ) if ( a r b r e == NULL) return NULL; if ( v a l e u r == a r b r e −>v a l e u r ) return a r b r e ; if ( v a l e u r < a r b r e −>v a l e u r ) ∗ return t r o u v e ( a r b r e −>gauche , v a l e u r ) ; else ∗ ∗ return t r o u v e ( a r b r e −>d r o i t e , v a l e u r ) ; I / / on descend à droite on / } 3 descend à { gauche ∗/ Exercice 10 (diculté : •) Écrire une fonction verie () qui renvoie un entier non nul si et seulement si l'arbre binaire passé en paramètre est un arbre binaire de recherche. Remarque : on pourra écrire une fonction auxiliaire (récursive) qui vérie qu'un arbre binaire (non vide) satisfait les propriétés d'ABR et en même temps détermine les valeurs minimales et maximales contenues dans cette arbre binaire (et les renvoie via des pointeurs en argument. . . ). Correction ∗ ∗ int v e r i f i e _ r e c ( arbre_t a r b r e , int ∗ min , int ∗max) { int i ; ∗ min = ∗ max = a r b r e −>v a l e u r ; if ( a r b r e −>gauche != NULL) ∗ ∗ if ( ! v e r i f i e _ r e c ( a r b r e −>gauche , &i , max) | | ! ( a r b r e −>v a l e u r > ∗max ) ) return 0 ; if ( a r b r e −>d r o i t != NULL) ∗ ∗ if ( ! v e r i f i e _ r e c ( a r b r e −>d r o i t , min , &i ) | | ! ( a r b r e −>v a l e u r <= ∗ min ) ) return 0 ; return 1 ; I / à n ' appeler que sur des arbres != NULL / / on utilise l ' évaluation paresseuse du || / / on utilise l ' évaluation paresseuse du || / } int int min , max ; return ( ( a r b r e == NULL) v e r i f i e ( arbre_t a r b r e ) { } ? 1 : v e r i f i e _ r e c ( a r b r e , &min , &max ) ) ; Exercice 11 (diculté : ••) Écrire une fonction tri () qui trie un tableau d'entiers donné en argument à l'aide d'un arbre binaire de recherche. Remarque : on pourra écrire une fonction auxiliaire récursive qui, à partir d'un sous-arbre d'un ABR et d'une position dans le tableau, remplit le tableau, à partir de la position donnée, avec les valeurs contenues dans ce sous-arbre binaire et renvoie le nombre de valeurs du sous-arbre. . . Correction int t r i _ r e c ( arbre_t a r b r e , int int j = 0 ; if ( a r b r e == NULL) return j ; I } void i, int j = t r i _ r e c ( a r b r e −>gauche , i , t a b l e a u ) ; t a b l e a u [ i + j ] = a r b r e −>v a l e u r ; j ++; j += t r i _ r e c ( a r b r e −>d r o i t , i + j , t a b l e a u ) ; j; return int int int tri ( taille , ∗ tableau ) { i; / ∗ ne p a s o u b l i e r d ' i n i t i a l i s e r à NULL arbre_t a r b r e = NULL; ( i = 0 ; i < t a i l l e ; i ++) arbre = i n s e r e ( arbre , tableau [ i ] ) ; t r i _ r e c ( arbre , 0 , tableau ) ; detruit_arbre ( arbre ) ; for } ∗ tableau ) { 4 l ' arbre initial ∗/ Exercice 12 Bonus (diculté : •••) Écrire une fonction supprime() qui supprime une valeur de l'arbre (on supprimera la première rencontrée) tout en conservant les propriétés d'ABR. L'algorithme est le suivant (une fois trouvé le n÷ud contenant la valeur en question) : si le n÷ud à enlever ne possède aucun ls, on l'enlève, si le n÷ud à enlever n'a qu'un ls, on le remplace par ce ls, si le noeud à enlever a deux ls, on le remplace par le sommet de plus petite valeur dans le sous-arbre droit, puis on supprime ce sommet. I Correction int arbre_t supprime ( arbre_t a r b r e , valeur ) { noeud_t noeud = a r b r e , ∗ p e r e = &a r b r e ; noeud_t nouveau_noeud , ∗ nouveau_pere ; ( noeud != NULL) { ( v a l e u r == noeud−>v a l e u r ) ; ( v a l e u r < noeud−>v a l e u r ) { p e r e = &noeud−>gauche ; noeud = noeud−>gauche ; } { / ∗ v a l e u r >= noeud −>v a l e u r ∗ / p e r e = &noeud−>d r o i t ; noeud = noeud−>d r o i t ; } } ( noeud != NULL) { ( noeud−>gauche == NULL) { ( noeud−>d r o i t == NULL) { ∗ p e r e = NULL; f r e e ( noeud ) ; } { / ∗ noeud −> d r o i t != NULL ∗ / ∗ p e r e = noeud−>d r o i t ; f r e e ( noeud ) ; } } { / ∗ noeud −>g a u c h e != NULL ∗ / ( noeud−>d r o i t == NULL) { ∗ p e r e = noeud−>gauche ; f r e e ( noeud ) ; } { / ∗ noeud −> d r o i t != NULL ∗ / / ∗ r e c h e r c h e d e l a p l u s p e t i t e v a l e u r du s o u s − a r b r e nouveau_noeud = noeud−>d r o i t ; nouveau_pere = &noeud−>d r o i t ; ( nouveau_noeud != NULL) ( nouveau_noeud−>gauche != NULL) { nouveau_pere = &nouveau_noeud−>gauche ; nouveau_noeud = nouveau_noeud−>gauche ; } noeud−>v a l e u r = nouveau_noeud−>v a l e u r ; ∗ nouveau_pere = nouveau_noeud−>d r o i t ; f r e e ( nouveau_noeud ) ; } } } arbre ; } while if break if else if if if else else if else while if return 5 droit ∗/