31
1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 [email protected] http://www.math-info.univ-paris5.fr/~delozanne/ Les arbres (3) : Parcours Cours 6

1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 [email protected] delozanne

Embed Size (px)

Citation preview

Page 1: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

1

Algorithmique et structures de données en C

É. Delozanne, L2-S4-prog4, Paris [email protected]

http://www.math-info.univ-paris5.fr/~delozanne/

Les arbres (3) : Parcours

Cours 6

Page 2: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

2

Parcourir un arbre ? parcours :

• examiner les nœuds d’un arbre pour effectuer un traitement

2 types de parcours classiques sur les arbres binairesen profondeur à main gauche (récursif par nature)

en largeur (itératif par nature)

Page 3: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

3

Plan du cours : parcours en profondeur à main gauche

• algorithme récursif général– cas particuliers (parcours préfixe, infixe, postfixe)

• algorithme itératif général : utilise une pile– application : calcul de la hauteur d'un arbre– simplification dans le cas d'un parcours préfixe

parcours en largeur (par niveaux, hiérarchique) : • utilise une file

récursivité et itération

Parcours d'arbres binaires : récursifs/itératifs

Page 4: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

4

Objectifs du cours (et TD) Mise en œuvre en C d’algorithmes (un peu)

complexes Culture informatique de base sur les algorithmes

récursifs et itératifs et sur la compilation Vue abstraite sur les parcours d’arbres pour savoir

les écrire plus facilement

Compétence à acquérir :• Savoir écrire rapidement et sans erreur les

algorithmes récursifs classiques de parcours d’arbres binaires

– Afficher, lire, construire, hauteur, taille, libérer la mémoire, tests etc.

• Savoir programmer un parcours en largeur

Page 5: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

5

Parcours en profondeur à main gauche

chemin qui descend toujours le plus à gauche possible chaque nœud est rencontré 3 fois

1. à la descente avant la visite du sous-arbre gauche– on applique au nœud le traitement 1

2. Après la visite du sous-arbre gauche et avant de descendre dans le sous-arbre droit – on applique au nœud le traitement 2

3. (et dernière fois) quand les deux sous-arbres ont été parcourus, en remontant à droite– on applique au nœud le traitement 3

si l'arbre est vide on lui applique un traitement appelé terminaison

Page 6: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

6

Parcours récursif en profondeur à main gauche

Algorithme Parcours (A) si A est vide

• alors terminaison(A)• sinon

1.  traitement1 (A)

2.  Parcours(sous-arbreGauche(A)

3.  traitement2 (A)

4.  Parcours(sous-arbreDroit(A)

5.  traitement3 (A)

Exemple :

void ArbinTracer(ARBIN A) {

if (ArbinVide(A)) /* terminaison */

printf("() "); else {

ElementAfficher(ArbinEtiquette(A)); /* traitement 1 */

ArbinTracer(ArbinGauche(A)) ;

ElementAfficher(ArbinEtiquette(A)); /* traitement 2 */

ArbinTracer(ArbinDroit(A)) ;

ElementAfficher(ArbinEtiquette(A)); /* traitement 3 */

}

}

Page 7: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

7

Cas particuliers

Lorsque le noeud est traité une seule fois Parcours préfixe (Racine Gauche Droit)

• le nœud racine est traité au premier passage avant le parcours des sous-arbres

Parcours infixe ou symétrique (Gauche Racine Droit)• le nœud racine est traité au second passage

– après le parcours du sous-arbre gauche – et avant le parcours du sous-arbre droit

Parcours postfixe (Gauche Droit Racine )• le nœud racine est traité en dernier après le

parcours des sous-arbres

Page 8: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

8

Parcours préfixe (R G D)

le nœud racine est traité au premier passage avant le parcours des sous-arbres

ParcoursPréfixe(A)• si A est vide

– alors terminaison(A)– sinon

traitement (A) ParcoursPréfixe(sous-arbreGauche de A) ParcoursPréfixe(sous-arbreDroit de A)

Page 9: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

9

Exemple : RGD

Afficher les étiquettes d'un arbre (ordre préfixe)• terminaison : afficher arbre vide () - juste pour voir• traitement 1 : afficher l'étiquette

void ArbinAfficherPref(ARBIN A) {

if (ArbinVide(A))

printf("() ");  /* terminaison */

else {

ElementAfficher(ArbinEtiquette(A)); /* traitement 1 */

ArbinAfficherPref(ArbinGauche(A)) ;

ArbinAfficherPref(ArbinDroit(A)) ;

}

}

Page 10: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

10

Parcours infixe ou symétrique (G R D)

le nœud racine est traité au second passage après le parcours du sous-arbre gauche et avant le parcours du sous-arbre droit

ParcoursInfixe(A)• si A est vide

– alors terminaison(A)– sinon

ParcoursInfixe(sous-arbreGauche de A) traitement (A) ParcoursInfixe(sous-arbreDroit de A)

Page 11: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

11

Exemple : GRD Afficher les étiquettes d'un arbre (ordre infixe)

• terminaison : afficher arbre vide () • traitement 2 : afficher l'étiquette

void ArbinAfficherInf(ARBIN A){if (ArbinVide(A))

printf("() ");  /* terminaison */else {ArbinAfficherInf(ArbinGauche(A)) ;ElementAfficher(ArbinEtiquette(A));

/* traitement 2 */ArbinAfficherInf(ArbinDroit(A)) ;

}}

Page 12: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

12

Parcours postfixe (G D R)

le nœud racine est traité au troisième passage après le parcours des sous-arbres gauche et droit

ParcoursPostfixe(A)• si A est vide

– alors terminaison(A)– sinon

ParcoursPostfixe(sous-arbreGauche de A) ParcoursPostfixe(sous-arbreDroit de A) traitement (A)

Page 13: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

13

Exemples : GDR Afficher les étiquettes d'un arbre (ordre postfixe)

• terminaison : afficher arbre vide • traitement 3 : afficher l'étiquette

code C

void ArbinAfficherPost(ARBIN A){

if (ArbinVide(A))

printf("() ");  /* terminaison */

else {

ArbinAfficherPost(ArbinGauche(A)) ;

ArbinAfficherPost(ArbinDroit(A)) ;

ElementAfficher(ArbinEtiquette(A)); /* traitement 3 */

}

}

Page 14: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

14

"Dérécursiver"

dérécursiver = • transformer un algorithme récursif en algorithme

itératif pour des raisons d'efficacité (économie de temps et de

mémoire) « on » peut être amené à "dérécursiver" certains algorithmes • « on » : le plus souvent les compilateurs

méthodes générales pour supprimer :• un appel récursif terminal :

– utilise une boucle tant que• un appel récursif non terminal :

– utilise une pile

Page 15: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

15

Parcours itératif en profondeur à main gauche

idée : une pile mémorisant le nœud courant et la direction future

1.descente (à gauche du nœud, 1ier passage)

• effectuer le traitement 1 sur la racine

• empiler le nœud et la direction future (descendre à dr., 2ième passage)

• descendre à gauche sur le sous-arbre gauche

2.remontée gauche (2ième passage)

• effectuer le traitement 2 sur la racine

• empiler le nœud et la direction future (remonter à droite, 3ième passage)

• descendre à gauche sur le sous-arbre droit

3.remontée droite (3ième passage)

• effectuer le traitement 3 sur la racine

• dépiler le nœud et le sens de la visite sur ce nœud

Page 16: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

16

Parcours itératif en profondeur à main gauche A = l'arbre à parcourir créer une pile vide P , direction = descente gauche ; fini = A non vide tant que (pas fini)

• Si A est vide – alors

terminaison Si P est vide alors fini sinon (A et direction) = dépiler

• si (pas fini) selon direction direction = descente gauche

– traitement 1, empiler (A, desc. dr), – A = ss-arbGauche (A) (desc. à g. sur le sous-arbre gauche)

direction = descente droite– traitement 2, empiler (A, montée. dr), – A = ss-arbDroit (A) , direction = descente gauche

(descendre à gauche sur le sous-arbre droit) direction = montée droite

– traitement 3, – Si P est vide alors fini sinon (A et direction) = dépiler

détruire la pile P

Page 17: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

17

Exemple : calcul de la hauteur d'un arbre

hauteur(A) (algorithme récursif)• si A est vide retourner -1• sinon

– hg = Hauteur(sous-arbreGauche(A)– hd = Hauteur(sous-arbreDroit(A)– si hg > hd

retourner hg +1 sinon retourner hd + 1

Page 18: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

18

Calcul itératif de la hauteur d'un arbre binaire A arbre à parcourir, h = -1, max = -1, créer une pile vide P , direction = descente gauche répéter jusqu'à Pile vide et direction = montée droite

• si A est vide alors– direction = montée droite– si h > max alors max = h

• si (Pile non vide ou direction ≠ montée droite) alors selon directiondirection = descente gauche

  h ++, empiler (A, desc. dr),  A = ss-arbGauche (A)

direction = descente droite h++, empiler (A, montée. dr),  A = ss-arbDroit (A) , direction = descente gauche

direction = montée droite h--, dépiler le nœud A et la direction

détruire P et retourner max

Page 19: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

19

Codage en C idée géniale : utiliser

• le TDA PILE (cours 3) et le TDA ARBRE BINAIRE (cours 5) problème

• le TDA ARBRE BINAIRE utilise un TDA ELEMENT incarné par des entiers (ou des caractères)• le TDA PILE utilise lui aussi un TDA ELEMENT mais incarné

par des couples (arbres, position) le souk !

• un même identificateur (ELEMENT) désigne deux types différents

solution• en C : bricolo• en C++ : les template (polymorphisme paramétrique)• en Smalltalk, Java : polymorphisme d’héritage

Page 20: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

20

Simplifications de l'algorithme général

exemple : Parcours préfixe itératif (à la Sedgewick= A arbre à parcourir créer une Pile vide P si l'arbre A est non vide empiler A tant que P est non vide répéter

• dépiler dans A• traiter (A)• si le sous-arbre droit est non vide, l'empiler• si le sous-arbre gauche est non vide, l'empiler

détruire (P)

Page 21: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

21

Plan du cours : parcours en profondeur à main gauche

algorithme récursif généralcas particuliers (parcours préfixe, infixe, postfixe)

algorithme itératif général : utilise une pileapplication : calcul de la hauteur d'un arbresimplification dans le cas d'un parcours préfixe

parcours en largeur (par niveaux, hiérarchique) : • utilise une file

récursivité et itération

Parcours d'arbres binaires : récursifs/itératifs

Page 22: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

22

Parcours en largeur (hiérarchique, par niveau) stratégie pas du tout récursive traitement des nœuds par niveau :

traiter les frères avant les fils dans la représentation graphique :

1. de gauche à droite

2. de haut vers le bas utilisation d'une file

• on traite un nœud• on fait entrer dans la file

– d’abord son fils gauche – puis son fils droit

Page 23: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

23

Algorithme

ParcoursEnLargeur (A) créer une file vide F si A est non vide alors

• entrer A dans F• tant que F non vide

1. A = sortir(F)

2. traiter (A)

3. si ss-arbGauche(A) non vide l'entrer dans F

4. si ss-arbDroit(A) non vide l'entrer dans F FinSi détruire F

Page 24: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

24

Affichage en largeur

void ArbinAfficherLargeur(ARBIN A){FFILE F = FileCreer(20) ; ELTSPCL elt ;if ( ! ArbinVide(A) ) {

elt = EltSpclInit(A,1) ; FileEntrer(elt, F) ;while ( ! FileVide (F) ) {

A = EltSpclArbre(FileSortir(F)) ;ElementAfficher(ArbinEtiquette(A));if( ! ArbinVide(ArbinGauche(A)) ) {

elt = EltSpclInit(ArbinGauche(A),1) ; FileEntrer(elt, F); }

if( ! ArbinVide(ArbinDroit(A)) ) {elt = EltSpclInit(ArbinDroit(A),1);FileEntrer(elt, F) ;}

}}

FileDetruire(F) ;}

Page 25: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

25

Plan du cours : parcours en profondeur à main gauche

algorithme récursif généralcas particuliers (parcours préfixe, infixe, postfixe)

algorithme itératif général : utilise une pileapplication : calcul de la hauteur d'un arbresimplification dans le cas d'un parcours préfixe

parcours en largeur (par niveaux, hiérarchique) : utilise une file

récursivité et itération

Parcours d'arbres binaires : récursifs/itératifs

Page 26: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

26

Y'avait longtemps... factorielle version récursive

long factorielle (int n) {

if (n < = 0) return 1 ;

else return n * factorielle (n - 1) ;

} factorielle version itérative

long factorielle (int n) {

int r = 1 ;

while ( n > 1) { r = r * n ; n -- ; }

return r ;

}

Page 27: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

27

Et sa copine ...fibonacci fibonacci version récursive

long fibonacci (int n) {

if (n < = 1) return 1 ;

else return fibonacci (n - 1) + fibonacci (n - 2) ;

} fibonacci version itérative

long fibonacci (int n) {

int u, v ; u = v = 1 ;

for (i = 3 ; i <= n ; i++) { u = u + v ; v = u - v ; }

return u ;

}

Page 28: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

28

Récursif versus itératif sur ces 2 exemples triviaux la solution itérative est

meilleure certains compilateurs astucieux remplacent

• l'appel récursif terminal par une boucle tant que• un appel non terminal par un algorithme itératif

utilisant une pile certains algorithmes sont « naturellement » récursifs

1) algorithmes récursifs de type diviser pour résoudre– 2 appels récursifs traitant chacun en gros la moitié

des données sans calcul redondant

2) algorithmes récursifs de type combiner pour résoudre– commencer par résoudre des cas triviaux – puis en les combinant résoudre des cas complexes

Page 29: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

29

Bilan un algorithme récursif peut être transformé en

algorithme itératif (ne le faire que si on voit que l'algorithme "patine" ou besoin impérieux)

un programme itératif n'est pas toujours plus efficace qu'un programme récursif

la récursivité est centrale• en informatique dite théorique pour distinguer

les problèmes (problèmes calculables) qu'une machine peut résoudre en temps fini • en IA et en recherche opérationnelle pour

résoudre des problèmes complexes• en programmation pour traiter de manière

simple de nombreux problèmes sur les arbres binaires (recherche, tri etc.)

Page 30: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

30

Retenir Un algorithme itératif n’est pas forcément plus

rapide qu’un algorithme itératif Certains algorithmes sont mieux traités par des

algorithmes itératifs D’autres sont mieux traités par des algorithmes

récursifs

Page 31: 1 Algorithmique et structures de données en C É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr delozanne

31

Auto-évaluation cours 6 Qu’est-ce que parcourir un arbre ? Quels sont les parcours classiques sur les arbres ? Les algorithmes itératifs sont-ils toujours plus

efficaces que les algorithmes récursifs ? Réciproque ? Donner des exemples de parcours en profondeur à

main gauche ? Même question pour le parcours hiérarchique Écrire en C les parcours classiques sur les arbres

binaires étudiés en cours, en TD