Algorithmique et structures de données en C

Slides:



Advertisements
Présentations similaires
La recherche de chemin optimal
Advertisements

Premier programme en C :
Tris.

Chapitre 3 Les arbres binaires
Chapitre annexe. Récursivité
Algorithmes et structures de données avancées Cours 6 Patrick Reuter
Portée des variables VBA & Excel
Fonctions & procédures
Introduction à l’Algorithmique
Calculs de complexité d'algorithmes
Cours n°2M2. IST-IE (S. Sidhom) UE 303 Promo. M2 IST-IE 2005/06 Conception dun système d'information multimédia Architecture trois-tiers : PHP/MySQL &
Exercices Algorithmiques
Algorithme et structure de données
Chap. 1 Structures séquentielles : listes linéaires
Chapitre VI. Arbres (définition, parcours, représentation)
ALGORITHMES RECURSIFS
IPA – Catherine Faron Zucke et Anne Marie Deryr. suite ordonnée d'éléments de taille variable ArrayList liste; liste = new ArrayList (); Ne peuvent contenir.
Introduction à la programmation (420-PK2-SL) cours 12 Gestion des applications Technologie de linformation (LEA.BW)
Récursivité.
Points importants de la semaine Les boucles. Les types arithmétiques. Les opérateurs.
44 Contrôle du déroulement du programme. 4-2 Objectifs A la fin de ce cours, vous serez capables de : Utiliser les constructions de prise de décision.
Cours d’Algorithmique

LOGO Responsable du cours Mlle Amina GHRAB : 1 ère année IAG Institut Supérieur de Gestion de Tunis.
OCaml – Les arbres L3 MI.
Dérécursivation Dérécursiver, c’est transformer un algorithme récursif en un algorithme équivalent ne contenant pas d’appels récursifs. Récursivité terminale.
Les structures de données arborescentes
II. Chaînage, SDD séquentielles
Analyse lexicale Généralités Expressions rationnelles Automates finis
Les algorithmes: complexité et notation asymptotique
IFT-2000: Structures de Données
Leçon 6 : Structures de données dynamiques IUP 2 Génie Informatique Méthode et Outils pour la Programmation Françoise Greffier.
RESUMES Module II1 SOMMAIRE CYCLE 1 : Saisir – Afficher – Données
1 Les pointeurs et quelques rappels sur certains éléments du langage C.
Les tris.
LES ARBRES IUP 2 Génie Informatique
Arbres Rouge noir Démo : INF3105 Structures de données et algorithmes
Présentation Structures de Données et TDA
Courbes de Bézier.
Traduction dirigée par la syntaxe
Notre calendrier français MARS 2014
COURS DE PROGRAMMATION ORIENTEE OBJET :
IFT-2000: Structures de données Piles et files Dominic Genest, 2009.
IV. Arbres  Arbre binaire (AB) Représentation SDD d’un AB
Structures de données IFT-2000 Abder Alikacem La récursivité Semaine 5 Département dinformatique et de génie logiciel Édition Septembre 2009.
LES ARBRES Un arbre est une structure homogène dont chaque élément,
III. Récursivité Principe et exemples
Structures de données IFT-2000 Abder Alikacem La récursivité Département d’informatique et de génie logiciel Édition Septembre 2009.
201 UMLV  Type « dictionnaire » sous-ensembles finis de E (ordonné) avec les opérations : Ens_vide :  Ens Ajouter : Ens x Elément  Ens Enlever.
Le langage C Structures de données
ECOLE DES HAUTES ETUDES COMMERCIALES MARKETING FONDAMENTAL
1 Notations Asymptotiques Et Complexité Notations asymptotiques : 0 et  Complexité des algorithmes Exemples de calcul de complexité.
10 paires -. 9 séries de 3 étuis ( n° 1 à 27 ) 9 positions à jouer 5 tables Réalisé par M..Chardon.
Ch. PAUL - Piles et Files à l'aide de listes chainées
CALENDRIER-PLAYBOY 2020.
USAM BRIDGE H O W E L L -CLASSIQUE
LES PILES ET FILES.
9 paires séries de 3 étuis ( n° 1 à 27 )
Quel est l’intérêt d’utiliser le diagramme de Gantt dans la démarche de projet A partir d’un exemple concret, nous allons pouvoir exploiter plusieurs parties.
Université de Sherbrooke
ITI1520 Lab #8: Récursivité Gilbert Arbez, Mohamad Eid, Daniel Amyot, Sylvia Boyd, Diana Inkpen et Alan Williams.
Arbres binaires et tables de hachage
Les algorithmes recursifs
La récursivité Mireille Goud HEG Vd AlgSD - Résurisivité.
CPI/BTS 2 Algorithmique & Programmation La récursivité Algo – Prog CPI/BTS2 – M. Dravet – 14/09/2003 Dernière modification: 14/09/2003.
UE MAREP Cours 8 : La pile d’exécution (Fonctions imbriquées et fonctions récursives) Patricia Renault UPMC 2005/2006.
UE MAREP Cours 10 : Projet Patricia Renault UPMC 2005/2006.
4/25/2017 4:30 PM Arbres (2,4) CSI2510 CSI2510.
Les arbres Arbres binaires
Transcription de la présentation:

Algorithmique et structures de données en C Cours 6 Les arbres (3) : Parcours É. Delozanne, L2-S4-prog4, Paris 5 Elisabeth.Delozanne@math-info.univ-paris5.fr http://www.math-info.univ-paris5.fr/~delozanne/ Rappel : Les programmes associés à ce cours figurent sur le polycopié joint. Ils sont accessibles sur diamant. Tous les polycopiés sont accessibles en ligne sur le site du module prog4 Site du module prog4 : http://www.math-info.univ-paris5.fr/~delozanne/ Programmes : \\diamant.ens.math-info.univ-paris5.fr\Data L2\prog4 Bibliographie des cours 4-5-6-7 sur les arbres : Pour les définitions et les analyses : Froidevaux (chapitre 7) Guerid et al. (chapitre 5) Pour le codage en C : Sedgewick (chapitres 4 et 5) Horowitz (chapitre 5) Quément (chapitre 5) Divay (chapitre 3)

en largeur (itératif par nature) Parcourir un arbre ? parcours : examiner les nœuds d’un arbre pour effectuer un traitement 2 types de parcours classiques sur les arbres binaires en profondeur à main gauche (récursif par nature) en largeur (itératif par nature) Pour un informaticien un arbre est une structure de données : les informations sont contenues dans les étiquettes des nœuds de l’arbre. Pour écrire des algorithmes effectuant des opérations sur ces informations on a besoin de parcourir tous les nœuds de l’arbre (parcours intégral) ou bien tous les nœuds jusqu’à ce que l’on trouve ce que l’on cherche (parcours de recherche). En prog1, prog2 et prog3 vous avez étudié de nombreux algorithme de parcours de tableaux (structures linéaires ; maintenant nous allons étudier les parcours classiques sur les arbres (structure hiérarchiques) Définition : On parcourt un arbre quand on examine tous les nœuds d'un arbre pour y effectuer un traitement. Nous étudierons les deux types de parcours les plus utilisés : - parcours en profondeur à main gauche ; nous en donnerons une version récursive puis une version itérative ; nous étudierons des cas particuliers que sont les parcours préfixe, infixe et postfixe d'un arbre binaire ; - parcours hiérarchique (en largeur, par niveaux) qui est un parcours itératif par nature.

Parcours d'arbres binaires : récursifs/itératifs 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 • Introduction : Cours 4 et 5 : Deux questions - Les arbres en théorie des graphes et les arbres informatiques ont-ils quelque chose en commun ? - leTDA Arbre Binaire et les différentes réalisations informatiques Cours 6 : Parcours d'arbres binaires, récursion, itération Cours 7 : les arbres n-aires • Références du cours 6 sur les parcours d'arbres - Froidevaux p. 114 - Sedgewick p. 48-69 - Les dessins sont copiés depuis http://rmdiscala.free.fr/livres/basesinfo4.3.pdf

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 Cours plus complexe que le précédent : vous avez encore 6 semaines pour l’assimiler…Il est probable que vous ne compreniez pas du premier coup ; il vous faudra étudier les algorithmes et les exemples après le cours et pendant les TD et peut être encore réviser.

Parcours en profondeur à main gauche chemin qui descend toujours le plus à gauche possible chaque nœud est rencontré 3 fois à la descente avant la visite du sous-arbre gauche on applique au nœud le traitement 1 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 (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 • Remarque : au deuxième passage on remonte à droite le ss-arbre gauche puis on descend à gauche dans le sous-arbre droit • exemple : tracer le parcours d’un arbre à main gauche traitement 1 = afficher l’étiquette de la racine de l'arbre suivi de 1 traitement 2 = afficher l’étiquette de la racine de l'arbre suivi de 2 traitement 3 = afficher l’étiquette de la racine de l'arbre suivi de 3 terminaison = afficher () Conseil : indiquer les fils vides a1 b1 d1 h1 () h2 () h3 d2 i1 () i2 () i3 d3 b2 e1 () e2 () e3 b3 a2 etc….

Parcours récursif en profondeur à main gauche Algorithme Parcours (A) si A est vide alors terminaison(A) sinon  traitement1 (A)  Parcours(sous-arbreGauche(A)  traitement2 (A)  Parcours(sous-arbreDroit(A)  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 */ } 2 appels récursifs : un sur le sous arbre gauche et un sur le sous-arbre droit exemple : tracer le parcours à main gauche

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

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) • Remarques 1) Par rapport à l'algorithme général traitement 1 et terminaison sont définis traitement 2 et 3 sont vides 2) Le parcours préfixe comporte 2 appels récursifs dont le dernier est dit terminal (aucun traitement ne le suit) et le premier est non terminal Exemple : cf TD et programmes en annexe ArbinAfficherPref, ArbinLire

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)) ; } a b d h () () i () () e () () c f () () g () ()

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) • Remarques 1) Par rapport à l'algorithme général traitement 2 et terminaison sont définis traitement 1 et 3 sont vides 2) Le parcours préfixe comporte 2 appels récursifs dont le dernier est dit terminal (aucun traitement ne le suit) et le premier est non terminal

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)) ; } () h () d () i () b () e () a () f () c () g ()

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) • Remarques 1) Par rapport à l'algorithme général : traitement 3 et terminaison sont définis et traitement 1 et 2 sont vides 2) Le parcours postfixe comporte 2 appels récursifs non terminaux (un traitement suit les 2 appels récursifs) Exemple (Cf cours 5) : rappel Destruction d’un arbre binaire : 1) libérer le sous-arbre gauche 2) libérer le sous-arbre droit 3) libérer la racine

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 */ } () () h ()() i d () () e b () () f () () g c a

"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 Remarques et rappel de prog3 : • Certains algorithmes récursifs sont aussi économiques (temps et mémoire) que des algorithmes itératifs exemple trivial mais prototypique : calcul d'une factorielle par une méthode récursive Autre exemple prototypique : les tours de Hanoï • Certains algorithmes récursifs sont une catastrophe (temps et mémoire) exemple trivial mais prototypique : calcul d'un terme de la suite de Fibonacci par une méthode récursive • Certains compilateurs intelligents qui cherchent à optimiser le code se chargent de dérécursiver les appels récursifs • Dans Sedgewick p. 48-69, vous trouverez une méthode générale pour dérécursiver un algorithme comportant 2 appels récursifs (au moins un est non terminal). Nous nous intéressons aux algorithmes adaptés aux parcours d'arbre binaires et nous présenterons deux méthodes pour dérécursiver des parcours d'arbres binaires : - une adaptée de Sedgewick p. 48-69 - une adaptée de Froidevaux p.114 Toutes ces méthodes utilisent une pile : - les algorithmes récursifs consomment de la pile système - les algorithmes dérécursivés eux consomment de la pile …programmée… elle a donc intérêt à être efficace notre pile ...pour être compétitive avec la pile système • Objectif : L'objectif de ce cours est de vous faire mieux comprendre les algorithmes de parcours d'arbres pas de vous inciter à dérecursiver les parcours d’abres binaires en profondeur à main gauche. L’objectif est aussi de pointer un "petit" problème de programmation en C pour l’utilisation de TDA « génériques ». • Retenir Un algorithme itératif n'est pas forcément plus rapide qu'un algorithme récursif, mais certains problèmes sont mieux traiter par des algorithmes itératifs et d'autres par des algorithmes récursifs

Parcours itératif en profondeur à main gauche idée : une pile mémorisant le nœud courant et la direction future 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 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 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 *** dessin algorithme appelé dans ce cours algorithme Allys-Delozanne arrêt du parcours: Pile vide et remontée droite (car la pile est vide au début ) une pile est une pile de couples (nœud, direction) ou (nœud, n° de passage) ce qui revient au même

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, détruire la pile P Exemples : - Dans les programmes du cours 4, l'affichage préfixe est donné • en version récursive avec deux appels terminaux, • en dérécursivant l'appel terminal avec un tant que • et enfin on donne 3 algorithmes itératifs utilisant une pile : le premier utilise l'algorithme général donné en cours, le deuxième dans le style Sedgewick et le troisième dans le style Froidevaux - en TD - suite du cours : calcul de la hauteur d'un arbre

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  Algorithme récursif bien connu de tous les programmeurs distingués Rappel : La hauteur d’un arbre est le nombre d’arcs sur la branche la plus longue La hauteur de l’arbre vide est -1 La hauteur d’un arbre composé d’un seul sommet est zéro Remarque : certains auteurs donnent une autre définition de la hauteur (ils ajoutent 1)   int ArbinHauteur(ARBIN A) { /* version récursive : les deux appels sont non terminaux */ int hg, hd ; if ( ArbinVide(A) ) return(-1) ; else { hg = ArbinHauteur(ArbinGauche(A)) ; hd = ArbinHauteur(ArbinDroit(A)) ; return(hd > hg ? hd + 1: hg +1) ; }

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 direction direction = 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 h--, dépiler le nœud A et la direction détruire P et retourner max idée : appliquer l'algorithme général - au début la hauteur h = -1 et la hauteur maximale aussi - quand on descend sur un sous-arbre (traitement 1 et 2) augmenter h - quand on remonte (traitement 3) diminuer h quand on arrive sur une feuille, on met à jour (si besoin) la longueur de la branche la plus longue (terminaison) : si h > l'ancien max alors max = h int HauteurITER (ARBIN A) { PILE P = PileCreer(200) ; ELTSPCL elt; int h = -1 ; /* hauteur du Noeud courant, au départ hautuer de l'arbre vide */ int max = -1; /* hauteur maximum */ int d ; /* d = 0 on descend à gauche; d = 1 remonte à gauche, d = 2 on remonte à droite */ d = 0 ; /* au début descend à gauche */ do { if (ArbinVide(A)) { /* terminaison */ d = 2 ; /* remonter à droite */ if (h > max) max = h ; } if (! (PileVide(P)&& d == 2)) { /* si pas fini */ switch (d) { case 0 : /* descendre à gauche */ h++; /* traitement 1 */ elt = EltSpclInit(A,1) ; PileEmpiler(elt, P); A = ArbinGauche (A) ; break ; case 1 : /* montée à gauche */ h++ ; /* traitement 2 */ elt = EltSpclInit(A,2) ; A = ArbinDroit (A) ; d = 0 ; /* descendre a gauche sur le ss arb droit */ case 2 : /* montée à droite */ h--; /* traitement 3 */ elt = PileDepiler (P) ; d = EltSpclDirection(elt) ; A = EltSpclArbre(elt) ; EltSpclDetruire(elt) ; } while( ! (PileVide(P)&& d == 2)) ; PileDetruire(P) ; return max ;

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 Solution en C++, ADA, Caml : le polymorphisme générique • en C++ : un programmeur peut définir de façon générique un type Pile <T> (T étant un type quelconque) c'est-à-dire qu'il définit une pile paramétrée par le type des éléments qu'elle contient ; on peut ensuite utiliser dans un même programme des piles de car et des piles d'arbres et des piles de couples et des piles de tout ce que l'on veut. On parle de polymorphisme paramétrique. en attendant les template du C++ Solution en C : sous-titre : là où le lecteur peut se convaincre, s'il ne l'était pas déjà, que - C n'est pas un langage de programmation de très haut niveau - que vos enseignants ont bien du mérite d'essayer de vous faire programmer abstrait en C Trêve de plaisanteries Quand je vous dis bricolo, c'est vraiment bricolo la preuve : 1/ ne pas toucher au TDA ARBRE BINAIRE du cours 4 (ouf quand même) 2/ modifier le TDA PILE pour qu'il n'utilise plus le TDA ELEMENT mais un TDA ELEMENTSPECIAL et remplacer ELEMENT par ELTSPCL dans tous les fichiers du TDA PILE 3/ incarner le TDA ELTSPCL en une structure dont le premier champ est un arbre et le deuxième une direction direction 0 : descente gauche (1ier passage) direction 1 : montée gauche (2ième passage) direction 2 : montée droite (3ième passage)

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) Simplifications possibles de l'algorithme itératif de parcours en profondeur à main gauche : 1) la terminaison peut s'effectuer sur les feuilles plutôt que sur l'arbre vide avant d'empiler tester si on est sur une feuille permet d'éviter d'empiler 2 fois les feuilles 2) dans le cas où le type de parcours est connu (pré, post, ou infixe, on n'a pas besoin d'empiler la direction (Cf. Sedgewick et programmes en annexe du cours AfficherPrefIter1)

Parcours d'arbres binaires : récursifs/itératifs 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 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 exemple : affichage par niveau des étiquettes d’un arbre Afficher a File : b c Sortir b et l’afficher et entrer ses fils dans la file le gauche en premier File : c d e Sortir c l’afficher et entrer ses fils le gauche en premier File : d e f Sortir d l’afficher Sortir e l’afficher Sortir f et l’afficher

Algorithme ParcoursEnLargeur (A) créer une file vide F si A est non vide alors entrer A dans F tant que F non vide A = sortir(F) traiter (A) si ss-arbGauche(A) non vide l'entrer dans F si ss-arbDroit(A) non vide l'entrer dans F FinSi détruire F Mise en œuvre en C voir les programmes annexe du cours 6 Remarque à ce propos : • dans la file les éléments sont des arbres (et pas des char et pas des couples de arbres direction) ; il faudrait donc bricoler à nouveau pour redéfinir un TDA ELEMENT TRES SPECIAL qui soit incarné par un arbre ; mais nous n’en avons pas eu le courage ! • ce n'est pas de la paresse c'est de la réutilisation de code déjà écrit ! et ça c'est très bien vu... le meilleur code est celui qui est déjà écrit (on ne nous paye pas pour faire des petits programmes inutiles mais pour vous enseigner la science informatique...) • nous avons réutilisé le TDA ELTSPCL couple (arbre , direction) bien qu'ici la direction soit tout à fait inutile ; faites donc comme si elle n'y était pas.

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) ;} Un grand classique à connaître

Parcours d'arbres binaires : récursifs/itératifs 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 • 

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 int r = 1 ; while ( n > 1) { r = r * n ; n -- ; } return r ; Complexité en temps : les deux versions sont en o(n) Complexité en espace mémoire : récursif : consomme de la pile système en o(n) itératif : 2 variables en o(1)

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 int u, v ; u = v = 1 ; for (i = 3 ; i <= n ; i++) { u = u + v ; v = u - v ; } return u ; itératif 1 2 3 4 5 6 7 8 … 1 1 2 3 5 8 13 21 … Complexité en temps en o(n) en espace en o(1) récursif : algorithme totalement inefficace dessiner l'arbre des appels récursifs.... en o( f n) où f est le nombre d'or (1, 61803....)

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 1) Algorithmes récursifs de type diviser pour résoudre démarche descendante (top-down) exemples : - recherche dichotomique dans une liste - calculs dans un arbre binaire (Cf. TD) : nombre de nœuds, nombre de feuilles, hauteur, teste et recherche sur un arbre binaire algorithmes récursifs on travaille sur le sous-arbre gauche et sur le sous-arbre droit ; ces algorithmes sont en o(n) dans le pire des cas - aussi bien en temps (on parcourt 1 fois chaque nœud) - qu'en mémoire : la profondeur de la récursion est en o(h) , hauteur de l'arbre algorithmes itératifs utilisent une pile et sont aussi en o(h), hauteur de l'arbre soit en o(n) dans le pire des cas ; ceci pour l'efficacité théorique ; en pratique il faut que la pile programmée de l'algorithme soit aussi efficace que la pile système (assez peu probable) En général : Quand un algorithme récursif de type diviser pour régner - sépare les données en 2 sous-ensembles disjoints et en gros de même taille - ne pas se fatiguer à le dérécursiver 2) Algorithmes récursifs de type combiner pour résoudre démarche ascendante (bottom-up) construire un arbre binaire en programmation fonctionnelle (construction à côté), on construit des arbres vides, des feuilles et petit à petit l'arbre grossit par assemblage de ces morceaux ; l'arbre final n'est construit qu'à la fin (Cf. ArbinLire dans les programmes en annexe du cours 6)

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.) Nous vous avons présenté les méthodes de dérécursivation non pas pour que vous dérécursiviez les algorithmes sur les arbres binaires (c'est, à la rigueur, un problème de compilateur pas de programmeur) mais - pour que vous appréhendiez mieux les rapports entre algorithmes itératifs et récursifs - pour que vous appréciez la récursivité pour sa simplicité et sa beauté… mais depuis que vous avez étudié les tours de Hanoï en prog3 vous êtes convaincus. Prochain cours : les arbres n-aires Dernier cours de notre cycle sur les arbres avant de les retrouver sous forme d'arbre binaire de recherche ou d’arbres lexicographiques pour représenter les dictionnaires à l’examen ??? en L3 et dans la vie professionnelle...

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

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