• Aucun résultat trouvé

Evaluation d’expressions & parcours d’arbres ´

5.2 Utilisation des arbres

5.2.1 Evaluation d’expressions & parcours d’arbres ´

Nous nous int´eressons ici `a la repr´esentation par arbre des expressions arithm´etiques. Dans cette repr´esentation, les nœuds de l’arbre sont les op´erateurs, et les feuilles les op´erandes. S’il n’y a que des op´erateurs d’arit´e 2 (comme + ou ×) on obtient alors un arbre binaire. Si on consid`ere des op´erateurs d’arit´e sup´erieur on obtient un arbre g´en´eral, mais on a vu que tout arbre est ´equivalent `a un arbre binaire. On ne consid`ere donc ici que des arbres binaires et des op´erateurs d’arit´e deux, mais les algorithmes seront les mˆemes pour des op´erateurs d’arit´e quelconque.

On ne s’int´eresse pas ici `a la construction d’un arbre d’´evaluation, mais uniquement `

a son ´evaluation. On part donc d’une arbre comme celui repr´esent´e sur la Figure 5.3. L’´evaluation d’une telle expression se fait en parcourant l’arbre, c’est-`a-dire en visitant chaque nœud de mani`ere syst´ematique. Il existe trois3 fa¸cons de parcourir un arbre. Cha- cune correspond `a une ´ecriture diff´erente de l’expression. Nous d´etaillons ci-apr`es ces trois parcours, qui sont essentiellement r´ecursifs, et font tous parti des parcours dits en profon-

deur (depth-first en anglais).

Parcours pr´efixe. Ce parcours consiste `a visiter la racine d’abord, puis le sous-arbre gauche, et enfin le sous-arbre droit. Il correspond `a l’´ecriture d’une expression dans laquelle les op´erateurs sont plac´es avant les op´erandes, par exemple × + 4 3 2 pour l’expression de la Figure 5.3. Cette notation est aussi appel´ee notation polonaise. L’algorithme suivant effectue un parcours pr´efixe de l’arbre A et pour chaque nœud visit´e affiche son contenu (pour afficher au final × + 4 3 2).

1 void preorder_traversal(tree A) {

2 if (A != NULL) {

3 printf("%d ", A->key);

3. Plus le parcours par niveau, appel´e ´egalement parcours en largeur (i.e. parcours du haut vers le bas,

en visitant tous les nœuds d’un mˆeme niveau de gauche `a droite avant de passer au niveau suivant. Ce

parcours n’est pas r´ecursif, et est utilis´e par exemple dans la structure de tas (cf. section 5.2.3), mais ne

4 preorder_traversal(A->left);

5 preorder_traversal(A->right);

6 }

7 }

Parcours infixe. Le parcours infixe correspond `a l’´ecriture habituelle des expressions arithm´etiques, par exemple (4 + 3) × 2 (sous r´eserve que l’on rajoute les parenth`eses n´ecessaires). Algorithmiquement, on visite d’abord le sous-arbre gauche, puis la racine, et enfin le sous-arbre droit :

1 void inorder_traversal(tree A) { 2 if (A != NULL) { 3 printf("("); 4 inorder_traversal(A->left); 5 printf("%d", A->key); 6 inorder_traversal(A->right); 7 printf(")"); 8 } 9 }

On est oblig´e d’ajouter des parenth`eses pour lever les ambigu¨ıt´es, ce qui rend l’expression un peu plus lourde : l’algorithme affichera ((4) + (3))× (2).

Parcours postfixe. Ce parcours consiste `a visiter le sous-arbre gauche d’abord, puis le droit, et enfin la racine. Il correspond `a l’´ecriture d’une expression dans laquelle les op´erateurs sont plac´es apr`es les op´erandes, par exemple 4 3 + 2× (c’est ce que l’on appel la notation polonaise inverse, bien connue des utilisateurs de calculatrices HP).

1 void postorder_traversal(tree A) { 2 if (A != NULL) { 3 postorder_traversal(A->left); 4 postorder_traversal(A->right); 5 printf("%d ", A->key); 6 } 7 }

Complexit´e. Pour les trois parcours ci-dessus, il est clair que l’on visite une fois chaque nœud, donc n appels r´ecursifs pour un arbre `a n nœuds, et ce quel que soit le parcours consid´er´e. La complexit´e de l’op´eration de parcours est donc en Θ(n).

Cas des arbres g´en´eraux. Les parcours pr´efixes et postfixes sont aussi bien d´efinis pour les arbres quelconques. Dans ce cas, la loi de parcours pr´efixe devient : visiter la racine, puis chacun des sous-arbres ; la loi de parcours postfixe devient : visiter chacun des

§ 5.2 Utilisation des arbres ENSTA – cours IN101 sous-arbres, puis la racine. Le parcours infixe ne peut en revanche pas ˆetre bien d´efini si le nombre de fils de chaque nœud est variable.

Notons aussi que le parcours postfixe d’un arbre g´en´eralis´e est ´equivalent au parcours infixe de l’arbre binaire ´equivalent (comme vu sur la Figure 5.2) et que le parcours pr´efixe d’un arbre g´en´eralis´e est ´equivalant au parcours pr´efixe de l’arbre binaire ´equivalent.

Parcours en largeur. Le parcours en largeur (breadth-first en anglais) est tr`es diff´erent des parcours en profondeur car il se fait par niveaux : on visite d’abord tous les nœuds du niveau 0 (la racine), puis ceux du niveau 1... Un tel parcours n’est pas r´ecursif mais doit ˆ

etre fait sur l’arbre tout entier. La technique la plus simple utilise deux files A et B. On initialise A avec la racine et B `a vide. Puis `a chaque ´etape du parcours on pop un nœud de la file A, on effectue l’op´eration que l’on veut dessus (par exemple afficher la clef), et on ajoute tous les fils de ce nœud `a B. On recommence ainsi jusqu’`a avoir vider A, et quand A et vide on inverse les files A et B et on recommence. On s’arrˆete quand les deux files sont vides.

`

A chaque ´etape la file A contient les nœuds du niveau que l’on est en train de parcourir, et la file B se remplit des nœuds du niveau suivant. On effectue donc bien un parcours par niveaux de l’arbre. Ce type de parcours n’est pas tr`es fr´equent sur des arbres et sera plus souvent rencontr´e dans le contexte plus g´en´eral des graphes.

Documents relatifs