Вы находитесь на странице: 1из 416

ALGORITHMIQUE ET PROGRAMMATION EN JAVA

Cours et exercices corrigs

Vincent Granet
Matre de confrences lESINA, lcole suprieure dingnieurs de luniversit de Nice-Sophia Antipolis

3e dition

Illustration de couverture : deep blue background Yurok Aleksandrovich - Fotolia.com

Dunod, Paris, 2000, 2004, 2010


ISBN 978-2-10-055322-8

Maud

Table des matires

AVANT-PROPOS CHAPITRE 1 INTRODUCTION 1.1 Environnement matriel 1.2 Environnement logiciel 1.3 Les langages de programmation 1.4 Construction des programmes 1.5 Dmonstration de validit CHAPITRE 2 ACTIONS LMENTAIRES 2.1 Lecture dune donne 2.2 Excution dune procdure prdnie 2.3 criture dun rsultat 2.4 Affectation dun nom un objet 2.5 Dclaration dun nom 2.5.1 Dclaration de constantes 2.5.2 Dclaration de variables 2.6 Rgles de dduction 2.6.1 Laffectation 2.6.2 Lappel de procdure 2.7 Le programme sinus crit en Java 2.8 Exercices CHAPITRE 3 TYPES LMENTAIRES 3.1 Le type entier 3.2 Le type rel

XV 1 1 4 5 10 12 15 15 16 17 17 18 18 19 19 19 20 20 22 23 24 25

VIII

Algorithmique et programmation en Java

3.3 3.4 3.5 3.6

Le type boolen Le type caractre Constructeurs de types simples Exercices

28 29 31 32 35 36 36 36 37 37 38 38 41 43 43 44 44 45 47 50 51 51 52 53 54 55 55 55 56 58 59 65 65 66 67 67 68 68 69

CHAPITRE 4 EXPRESSIONS 4.1 valuation 4.1.1 Composition du mme oprateur plusieurs fois 4.1.2 Composition de plusieurs oprateurs diffrents 4.1.3 Parenthsage des parties dune expression 4.2 Type dune expression 4.3 Conversions de type 4.4 Un exemple 4.5 Exercices CHAPITRE 5 NONCS STRUCTURS 5.1 nonc compos 5.2 noncs conditionnels 5.2.1 nonc choix 5.2.2 nonc si 5.3 Rsolution dune quation du second degr 5.4 Exercices CHAPITRE 6 PROCDURES ET FONCTIONS 6.1 Intrt 6.2 Dclaration dun sous-programme 6.3 Appel dun sous-programme 6.4 Transmission des paramtres 6.4.1 Transmission par valeur 6.4.2 Transmission par rsultat 6.5 Retour dun sous-programme 6.6 Localisation 6.7 Rgles de dduction 6.8 Exemples CHAPITRE 7 PROGRAMMATION PAR OBJETS 7.1 Objets et classes 7.1.1 Cration des objets 7.1.2 Destruction des objets 7.1.3 Accs aux attributs 7.1.4 Attributs de classe partags 7.1.5 Les classes en Java 7.2 Les mthodes

Table des matires

IX

7.2.1 Accs aux mthodes 7.2.2 Constructeurs 7.2.3 Les mthodes en Java 7.3 Assertions sur les classes 7.4 Exemples 7.4.1 quation du second degr 7.4.2 Date du lendemain 7.5 Exercices CHAPITRE 8 NONCS ITRATIFS 8.1 Forme gnrale 8.2 Lnonc tantque 8.3 Lnonc rpter 8.4 Finitude 8.5 Exemples 8.5.1 Factorielle 8.5.2 Minimum et maximum 8.5.3 Division entire 8.5.4 Plus grand commun diviseur 8.5.5 Multiplication 8.5.6 Puissance 8.6 Exercices CHAPITRE 9 LES TABLEAUX 9.1 Dclaration dun tableau 9.2 Dnotation dun composant de tableau 9.3 Modication slective 9.4 Oprations sur les tableaux 9.5 Les tableaux en Java 9.6 Un exemple 9.7 Les chanes de caractres 9.8 Exercices CHAPITRE 10 LNONC ITRATIF POUR 10.1 Forme gnrale 10.2 Forme restreinte 10.3 Les noncs pour de Java 10.4 Exemples 10.4.1 Le schma de H ORNER 10.4.2 Un tri interne simple 10.4.3 Confrontation de modle 10.5 Complexit des algorithmes 10.6 Exercices

70 70 71 72 73 73 76 79 81 81 82 83 84 84 84 85 85 86 87 87 88 91 91 92 93 93 93 95 97 98 101 101 102 102 103 103 105 106 109 111

Algorithmique et programmation en Java

CHAPITRE 11 LES TABLEAUX PLUSIEURS DIMENSIONS 11.1 Dclaration 11.2 Dnotation dun composant de tableau 11.3 Modication slective 11.4 Oprations 11.5 Tableaux plusieurs dimensions en Java 11.6 Exemples 11.6.1 Initialisation dune matrice 11.6.2 Matrice symtrique 11.6.3 Produit de matrices 11.6.4 Carr magique 11.7 Exercices CHAPITRE 12 HRITAGE 12.1 Classes hritires 12.2 Rednition de mthodes 12.3 Recherche dun attribut ou dune mthode 12.4 Polymorphisme et liaison dynamique 12.5 Classes abstraites 12.6 Hritage simple et multiple 12.7 Hritage et assertions 12.7.1 Assertions sur les classes hritires 12.7.2 Assertions sur les mthodes 12.8 Relation dhritage ou de clientle 12.9 Lhritage en Java CHAPITRE 13 LES EXCEPTIONS 13.1 mission dune exception 13.2 Traitement dune exception 13.3 Le mcanisme dexception de Java 13.3.1 Traitement dune exception 13.3.2 mission dune exception 13.4 Exercices CHAPITRE 14 LES FICHIERS SQUENTIELS 14.1 Dclaration de type 14.2 Notation 14.3 Manipulation des chiers 14.3.1 criture 14.3.2 Lecture 14.4 Les chiers de Java 14.4.1 Fichiers doctets 14.4.2 Fichiers dobjets lmentaires

115 115 116 116 117 117 118 118 118 119 120 122 127 127 130 131 131 133 134 134 134 135 135 135 139 139 140 141 141 142 142 145 146 146 147 147 148 149 149 150

Table des matires

XI

14.4.3 Fichiers dobjets structurs 14.5 Les chiers de texte 14.6 Les chiers de texte en Java 14.7 Exercices CHAPITRE 15 RCURSIVIT 15.1 Rcursivit des actions 15.1.1 Dnition 15.1.2 Finitude 15.1.3 criture rcursive des sous-programmes 15.1.4 La pile dvalution 15.1.5 Quand ne pas utiliser la rcursivit ? 15.1.6 Rcursivit directe et croise 15.2 Rcursivit des objets 15.3 Exercices CHAPITRE 16 STRUCTURES DE DONNES 16.1 Dnition dun type abstrait 16.2 Limplantation dun type abstrait 16.3 Utilisation du type abstrait CHAPITRE 17 STRUCTURES LINAIRES 17.1 Les listes 17.1.1 Dnition abstraite 17.1.2 Limplantation en Java 17.1.3 numration 17.2 Les piles 17.2.1 Dnition abstraite 17.2.2 Limplantation en Java 17.3 Les les 17.3.1 Dnition abstraite 17.3.2 Limplantation en Java 17.4 Les dques 17.4.1 Dnition abstraite 17.4.2 Limplantation en Java 17.5 Exercices CHAPITRE 18 GRAPHES 18.1 Terminologie 18.2 Dnition abstraite dun graphe 18.3 Limplantation en Java 18.3.1 Matrice dadjacence 18.3.2 Listes dadjacence

154 155 156 159 161 162 162 162 162 165 166 168 170 173 175 176 178 180 183 183 184 185 196 199 199 200 203 203 204 205 205 206 207 211 212 213 214 215 218

XII

Algorithmique et programmation en Java

18.4 Parcours dun graphe 18.4.1 Parcours en profondeur 18.4.2 Parcours en largeur 18.4.3 Programmation en Java des parcours de graphe 18.5 Exercices CHAPITRE 19 STRUCTURES ARBORESCENTES 19.1 Terminologie 19.2 Les arbres 19.2.1 Dnition abstraite 19.2.2 Limplantation en Java 19.2.3 Algorithmes de parcours dun arbre 19.3 Arbre binaire 19.3.1 Dnition abstraite 19.3.2 Limplantation en Java 19.3.3 Parcours dun arbre binaire 19.4 Reprsentation binaire des arbres gnraux 19.5 Exercices CHAPITRE 20 TABLES 20.1 Dnition abstraite 20.1.1 Ensembles 20.1.2 Description fonctionnelle 20.1.3 Description axiomatique 20.2 Reprsentation des lments en Java 20.3 Reprsentation par une liste 20.3.1 Liste non ordonne 20.3.2 Liste ordonne 20.3.3 Recherche dichotomique 20.4 Reprsentation par un arbre ordonn 20.4.1 Recherche dun lment 20.4.2 Ajout dun lment 20.4.3 Suppression dun lment 20.5 Les arbres AVL 20.5.1 Rotations 20.5.2 Mise en uvre 20.6 Arbres 2-3-4 et bicolores 20.6.1 Les arbres 2-3-4 20.6.2 Mise en uvre en Java 20.6.3 Les arbres bicolores 20.6.4 Mise en uvre en Java 20.7 Tables dadressage dispers 20.7.1 Le problme des collisions

219 219 220 221 223 225 226 227 228 229 231 232 234 235 237 239 240 243 244 244 244 244 244 246 246 248 250 252 252 253 254 256 256 259 264 264 267 269 273 279 280

Table des matires

XIII

20.7.2 Choix de la fonction dadressage 20.7.3 Rsolution des collisions 20.8 Exercices CHAPITRE 21 FILES AVEC PRIORIT 21.1 Dnition abstraite 21.2 Reprsentation avec une liste 21.3 Reprsentation avec un tas 21.3.1 Premier 21.3.2 Ajouter 21.3.3 Supprimer 21.3.4 Limplantation en Java 21.4 Exercices CHAPITRE 22 ALGORITHMES DE TRI 22.1 Introduction 22.2 Tris internes 22.2.1 Limplantation en Java 22.2.2 Mthodes par slection 22.2.3 Mthodes par insertion 22.2.4 Tri par changes 22.2.5 Comparaisons des mthodes 22.3 Tris externes 22.4 Exercices CHAPITRE 23 ALGORITHMES SUR LES GRAPHES 23.1 Composantes connexes 23.2 Fermeture transitive 23.3 Plus court chemin 23.3.1 Algorithme de Dijkstra 23.3.2 Algorithme A* 23.3.3 Algorithme IDA* 23.4 Tri topologique 23.4.1 Limplantation en Java 23.4.2 Existence de cycle dans un graphe 23.4.3 Tri topologique inverse 23.4.4 Limplantation en Java 23.5 Exercices CHAPITRE 24 ALGORITHMES DE RTRO-PARCOURS 24.1 criture rcursive 24.2 Le problme des huit reines 24.3 criture itrative

281 282 286 289 289 290 290 291 292 293 294 297 299 299 300 300 301 306 311 315 317 320 323 323 325 327 327 332 334 335 337 338 338 339 339 343 343 345 347

XIV

Algorithmique et programmation en Java

24.4 Problme des sous-suites 24.5 Jeux de stratgie 24.5.1 Stratgie MinMax 24.5.2 Coupure 24.5.3 Profondeur de larbre de jeu 24.6 Exercices CHAPITRE 25 INTERFACES GRAPHIQUES 25.1 Systmes interactifs 25.2 Conception dune application interactive 25.3 Environnements graphiques 25.3.1 Systme de fentrage 25.3.2 Caractristiques des fentres 25.3.3 Botes outils 25.3.4 Gnrateurs 25.4 Interfaces graphiques en Java 25.4.1 Une simple fentre 25.4.2 Convertisseur deuros 25.4.3 Un composant graphique pour visualiser des couleurs 25.4.4 Applets 25.5 Exercices BIBLIOGRAPHIE INDEX

348 350 350 353 356 357 361 361 363 365 365 367 370 371 372 372 374 377 381 383 385 389

Avant-propos

Depuis la premire dition de ce livre, septembre 2000, voil 10 ans, le langage JAVA a volu sur de nombreux points. En particulier, sa version 5.0, sortie en septembre 2004, a introduit dans le langage, entre autres, les notions trs importantes de gnricit, de boucle gnrale foreach, dnumration ou encore dautoboxing. Cette troisime dition d Algorithmique et Programmation en Java a t entirement rvise pour donner au lecteur de nouvelles mises en uvre des types abstraits et des algorithmes utilisant les nouvelles proprits du langage JAVA jusqu sa version 6. Par ailleurs, des corrections, des modications et des ajouts de nouveaux algorithmes, notamment dans le chapitre Algorithmes sur les graphes , ont t galement apports cette troisime dition. Dautre part, ladresse du site W EB du livre a chang. Le lecteur pourra trouver cette nouvelle adresse des corrigs dexercice, des applets et le paquetage algo.java qui contient les classes JAVA dimplmentation de tous les types abstraits prsents dans le livre.
elec.polytech.unice.fr/~vg/fr/livres/algojava

Je tiens remercier tout particulirement Carine Fdle qui, une fois encore, a relu de faon attentive cet ouvrage pour y traquer les dernires erreurs et me faire part de ses remarques toujours constructives. Enn, je remercie toute lquipe Dunod, Carole Trochu, Jean-Luc Blanc, et Romain Hennion, pour leur aide prcieuse et leurs conseils aviss quils ont apports aux trois ditions de cet ouvrage ces dix dernires annes. Sophia Antipolis, fvrier 2010. La seconde dition de cet ouvrage ma donn loccasion de complter un de ses chapitres, den ajouter un autre et de corriger des erreurs. Une section sur les arbres quilibrs, qui manquait au chapitre 20 consacr aux tables, a t introduite. Elle traite les arbres AVL, 2-3-4 et bicolores et prsente les algorithmes dajout et de suppression, ainsi que leur programmation en JAVA (rarement publie dans la littrature). Par ailleurs, certains lecteurs regrettaient labsence dun chapitre sur les interfaces graphiques. Le chapitre 25 comble cette lacune. Enn,

XVI

Algorithmique et programmation en Java

de nombreuses, mais lgres, retouches (corrections de coquilles, clarication du texte, etc.) ont t galement apportes ce livre. Sophia Antipolis, mars 2004. Linformatique est une science mais aussi une technologie et un ensemble doutils. Ces trois composantes ne doivent pas tre confondues, et lenseignement de linformatique ne doit pas tre rduit au seul apprentissage des logiciels. Ainsi, lactivit de programmation ne doit pas se confondre avec ltude dun langage de programmation particulier. Mme si limportance de ce dernier ne doit pas tre sous-estime, il demeure un simple outil de mise en uvre de concepts algorithmiques et de programmation gnraux et fondamentaux. Lobjectif de cet ouvrage est denseigner au lecteur des mthodes et des outils de construction de programmes informatiques valides et ables. Ltude de lalgorithmique et de la programmation est un des piliers fondamentaux sur lesquels repose lenseignement de linformatique. Ce livre sadresse principalement aux tudiants des cycles informatiques et lves ingnieurs informaticiens, mais aussi tous ceux qui ne se destinent pas la carrire informatique mais qui seront certainement confronts au dveloppement de programmes informatiques au cours de leur scolarit ou dans leur vie professionnelle. Ce livre correspond au cours dalgorithmique et de programmation qui stend sur les deux premires annes du premier cycle dingnieur de lE SINSA, cole Suprieure dIngnieurs de luniversit de Nice-Sophia Antipolis. Les quinze premiers chapitres sont le cours de premire anne. Ils prsentent les concepts de base de la programmation imprative, en sappuyant sur une mthodologie objet. Ils mettent en particulier laccent sur la notion de preuve des programmes grce la notion dafrmations (antcdent, consquent, invariant) dont la vrication formelle garantit la validit de programmes. Ils introduisent aussi la notion de complexit des algorithmes pour valuer leur performance. Les dix derniers chapitres correspondent au cours de deuxime anne. Ils tudient en dtail les structures de donnes abstraites classiques et un certain nombre dalgorithmes fondamentaux que tout tudiant en informatique doit connatre et matriser. La prsentation des concepts de programmation cherche tre indpendante, autant que faire se peut, dun langage de programmation particulier. Les algorithmes seront dcrits dans une notation algorithmique pure. Pour des raisons pdagogiques, il a toutefois bien fallu faire le choix dun langage pour programmer les structures de donnes et les algorithmes prsents dans cet ouvrage. Ce choix sest port sur le langage objets JAVA [GJS96], non pas par effet de mode, mais plutt pour les qualits de ce langage, malgr quelques dfauts. Ses qualits sont en particulier sa relative simplicit pour la mise en uvre des algorithmes, un large champ dapplication et sa grande disponibilit sur des environnements varis. Ce dernier point est en effet important ; le lecteur doit pouvoir disposer facilement dun compilateur et dun interprte an de rsoudre les exercices proposs la n des chapitres. Pour les dfauts, on peut par exemple regretter labsence de lhritage multiple et de la gnricit, et la prsence de constructions archaques hrites du langage C. Ce livre nest pas un ouvrage dapprentissage du langage JAVA. Mme si les lments du langage ncessaires la mise en uvre des notions dalgorithmique et de programmation ont t introduits, ce livre nenseignera pas au lecteur les nesses et les arcanes de JAVA, pas plus quil ne dcrira les

Avant-propos

XVII

nombreuses classes de lAPI. Le lecteur intress pourra se reporter aux trs nombreux ouvrages qui dcrivent le langage en dtail, comme par exemple [Bro99, Ska00, Eck00]. Les corrigs de la plupart des exercices, ainsi que des applets qui proposent une vision graphique de certains programmes prsents dans louvrage sont accessibles sur le site W EB de lauteur ladresse :
elec.polytech.unice.fr/~vg/fr/livres/algojava

Ce livre doit beaucoup de nombreuses personnes. Tout dabord, aux auteurs des algorithmes et des techniques de programmation quil prsente. Il nest pas possible de les citer tous ici, mais les rfrences leurs principaux textes sont dans la bibliographie. Olivier Lecarme et Jean-Claude Boussard, mes professeurs luniversit de Nice qui mont enseign cette discipline au dbut des annes 80. Je tiens tout particulirement remercier ce dernier qui fut le tout premier lecteur attentif de cet ouvrage alors quil ntait encore quune bauche, et qui ma encourag poursuivre sa rdaction. Johan Montagnat, qui est lorigine de plusieurs exercices de n de chapitre. Carine Fdle qui a bien voulu lire et corriger ce texte de nombreuses reprises ; quelle en soit spcialement remercier. Enn, mes collgues et mes tudiants qui mont aid et soutenu dans cette tche ardue quest la rdaction dun livre.

Sophia Antipolis, juin 2000.

Chapitre 1

Introduction

Les informaticiens, ou les simples usagers de loutil informatique, utilisent des systmes informatiques pour concevoir ou excuter des programmes dapplication. Nous considrerons quun environnement informatique est form dune part dun ordinateur et de ses quipements externes, que nous appellerons environnement matriel, et dautre part dun systme dexploitation avec ses programmes dapplication, que nous appellerons environnement logiciel. Les programmes qui forment le logiciel rclament des mthodes pour les construire, des langages pour les rdiger et des outils pour les excuter sur un ordinateur. Dans ce chapitre, nous introduirons la terminologie et les notions de base des ordinateurs et de la programmation. Nous prsenterons les notions denvironnement de dveloppement et dexcution dun programme, nous expliquerons ce quest un langage de programmation et nous introduirons les mthodes de construction des programmes.

1.1

ENVIRONNEMENT MATRIEL

Un automate est un ensemble ni de composants physiques pouvant prendre des tats identiables et reproductibles en nombre ni, auquel est associ un ensemble de changements dtats non instantans quil est possible de commander et denchaner sans intervention humaine. Un ordinateur est un automate dterministe composants lectroniques. Tous les ordinateurs, tout au moins, les ordinateurs monoprocesseurs, sont construits, peu ou prou, sur le modle du mathmaticien amricain dorigine hongroise VON N EUMANN propos en 1944. Un ordinateur est muni : Dune mmoire, dite centrale ou principale, qui contient deux sortes dinformations : dune part linformation traitante, les instructions, et dautre part linformation traite,

Chapitre 1 Introduction

les donnes. Cette mmoire est forme dun ensemble de cellules, ou mots, ayant chacun une adresse unique, et contenant des instructions ou des donnes. La reprsentation de linformation est faite grce une codication binaire, 0 ou 1. On appelle longueur de mot, caractristique dun ordinateur, le nombre dlments binaires, appels bits, groups dans une simple cellule. Les longueurs de mots usuelles des ordinateurs actuels (ou passs) sont, par exemple, 8, 16, 24, 32, 48 ou 64 bits. Cette mmoire possde une capacit nie, exprime en giga-octets (Go) ; un octet est un ensemble de 8 bits, un kilooctet (Ko) est gal 1024 octets, un mga-octet est gal 1024 Ko, un giga-octet (Go) est gal 1024 Mo, et enn un tera-octet (To) est gal 1024 Go. Actuellement, les tailles courantes des mmoires centrales des ordinateurs individuels varient entre 2 Go 4 Go1 . Dune unit centrale de traitement, forme dune unit de commande (UC) et dune unit arithmtique et logique (UAL). Lunit de commande extrait de la mmoire centrale les instructions et les donnes sur lesquelles portent les instructions ; elle dclenche le traitement de ces donnes dans lunit arithmtique et logique, et ventuellement range le rsultat en mmoire centrale. Lunit arithmtique et logique effectue sur les donnes quelle reoit les traitements commands par lunit de commande. De registres. Les registres sont des units de mmorisation, en petit nombre (certains ordinateurs nen ont pas), qui permettent lunit centrale de traitement de ranger de faon temporaire des donnes pendant les calculs. Laccs ces registres est trs rapide, beaucoup plus rapide que laccs une cellule de la mmoire centrale. Le rapport entre les temps daccs un registre et la mmoire centrale est de lordre de 100. Dunit dchanges relies des priphriques pour changer de linformation avec le monde extrieur. Lunit de commande dirige les units dchange lorsquelle rencontre des instructions dentre-sortie. Jusquau milieu des annes 2000, les constructeurs taient engags dans une course la vitesse avec des micro-processeurs toujours plus rapides. Toutefois, les limites de la physique actuelle ont t atteintes et depuis 2006 la tendance nouvelle est de placer plusieurs, le plus possible, microprocesseurs sur une mme puce (le circuit-intgr). Ce sont par exemple les processeurs 64 bits Core 2 dI NTEL ou K10 dAMD qui possdent de 2 4 processeurs. Les ordinateurs actuels possdent aussi plusieurs niveaux de mmoire. Ils introduisent, entre le processeur et la mmoire centrale, des mmoires dites caches qui acclrent laccs aux donnes. Les mmoires caches peuvent tre primaires, cest--dire situes directement sur le processeur, ou secondaires, cest--dire situes sur la carte mre. Certains ordinateurs, comme le K10, introduisent mme un troisime niveau de cache. En gnral, le rapport entre le temps daccs entre les deux premiers niveaux de mmoire cache est denviron 10. Le rapport entre le temps daccs entre la mmoire cache secondaire et la mmoire centrale est lui aussi denviron 10. Les quipements externes, ou priphriques, sont un ensemble de composants permettant de relier lordinateur au monde extrieur, et notamment ses utilisateurs humains. On peut distinguer : Les dispositifs qui servent pour la communication avec lhomme (clavier, cran, im1 Notez quavec 32 bits, lespace adressage est de 4 Go mais quen gnral les systmes dexploitation ne permettent dutiliser quun espace mmoire de taille infrieure. Les machines 64 bits actuelles, avec un systme dexploitation adapt, permettent des tailles de mmoire centrale suprieures 4 Go.

1.1

Environnement matriel

unit centrale

registres

unit de commande

instructions

unit arithmtique et logique

donnes rsultats

mmoire centrale

unit dchange

priphriques
F IG . 1.1 Structure gnrale dun ordinateur.

primantes, micros, haut-parleurs, scanners, etc.) et qui demandent une transcodication approprie de linformation, par exemple sous forme de caractres alphanumriques. Les mmoires secondaires qui permettent de conserver de linformation, impossible garder dans la mmoire centrale de lordinateur faute de place, ou que lon dsire conserver pour une priode plus ou moins longue aprs larrt de lordinateur. Les mmoires secondaires sont les disques durs, les CD-ROM, les DVD, les Blu-Ray ou les cls USB. Par le pass, les bandes magntiques ou les disquettes taient des supports trs utiliss. Actuellement les bandes magntiques le sont beaucoup moins, et la fabrication des disquettes (support de trs petite capacit et peu able) a t arrte depuis plusieurs annes dj. Aujourdhui, la capacit des mmoires secondaires atteint des valeurs toujours plus importantes. Alors que certains DVD ont une capacit de 17 Go, quun disque Blu-Ray atteint 50 Go, et que des cls USB de 64 Go sont courantes, un seul disque dur peut mmoriser jusqu 1 To. Des systmes permettent de regrouper plusieurs disques, vus comme un disque unique, offrant une capacit de plusieurs dizaines de tera-octets. Toutefois, laccs aux informations sur les supports secondaires restent bien plus lent que celui aux informations places en mmoire centrale. Pour les disques durs, le rapport est denviron 10. Les dispositifs qui permettent lchange dinformations sur un rseau. Pour relier, lordinateur au rseau, il existe par exemple des connexions laires comme celles de type ethernet, ou des connexions sans l comme celles de type WI-FI. Le lecteur intress par larchitecture et lorganisation des ordinateurs pourra lire avec prot le livre dA. TANENBAUM [Tan06] sur le sujet.

Chapitre 1 Introduction

1.2

ENVIRONNEMENT LOGICIEL

Lordinateur que fabrique le constructeur est une machine incomplte, laquelle il faut ajouter, pour la rendre utilisable, une quantit importante de programmes varis, qui constitue le logiciel. En gnral, un ordinateur est livr avec un systme dexploitation. Un systme dexploitation est un programme, ou plutt un ensemble de programmes, qui assure la gestion des ressources, matrielles et logicielles, employes par le ou les utilisateurs. Un systme dexploitation a pour tche la gestion et la conservation de linformation (gestion des processus et de la mmoire centrale, systme de gestion de chiers) ; il a pour rle de crer lenvironnement ncessaire lexcution dun travail, et est charg de rpartir les ressources entre les usagers. Il propose aussi de nombreux protocoles de connexion pour relier lordinateur un rseau. Entre lutilisateur et lordinateur, le systme dexploitation propose une interface textuelle au moyen dun interprte de commandes et une interface graphique au moyen dun gestionnaire de fentres. Les systmes dexploitation des premiers ordinateurs ne permettaient lexcution que dune seule tche la fois, selon un mode de fonctionnement appel traitement par lots qui assurait lenchanement de lexcution des programmes. partir des annes 60, les systmes dexploitation ont cherch exploiter au mieux les ressources des ordinateurs en permettant le temps partag, pour offrir un accs simultan plusieurs utilisateurs. Jusquau dbut des annes 80, les systmes dexploitation taient dits propritaires . Les constructeurs fournissaient avec leurs machines un systme dexploitation spcique et le nombre de systmes dexploitation diffrents tait important. Aujourdhui, ce nombre a considrablement rduit, et seuls quelques-uns sont rellement utiliss dans le monde. Citons, par exemple, W INDOWS et M AC O S pour les ordinateurs individuels, et U NIX pour les ordinateurs multi-utilisateurs. Aujourdhui avec laugmentation de la puissance des ordinateurs personnels, et lavnement des rseaux mondiaux, les systmes dexploitation offrent, en plus des fonctions dj cites, une quantit extraordinaire de services et doutils aux utilisateurs. Ces systmes dexploitation modernes mettent la disposition des utilisateurs tout un ensemble dapplications (traitement de texte, tableurs, outils multimdia, navigateurs pour le W EB, ...) qui leur offrent un environnement de travail pr-construit, confortable et facile dutilisation. Le traitement de linformation est lexcution par lordinateur dune srie nie de commandes prpares lavance, le programme, et qui vise calculer et rendre des rsultats, gnralement, en fonction de donnes entres au dbut ou en cours dexcution par lintermdiaire dinterfaces textuelles ou graphiques. Les commandes qui forment le programme sont dcrites au moyen dun langage. Si ces commandes se suivent strictement dans le temps, et ne sexcutent jamais simultanment, lexcution est dite squentielle, sinon elle est dite parallle. Chaque ordinateur possde un langage qui lui est propre, appel langage machine. Le langage machine est un ensemble de commandes lmentaires reprsentes en code binaire quil est possible de faire excuter par lunit centrale de traitement dun ordinateur donn. Le seul langage que comprend lordinateur est son langage machine. Tout logiciel est crit laide dun ou plusieurs langages de programmation. Un langage

1.3

Les langages de programmation

de programmation est un ensemble dnoncs dterministes, quil est possible, pour un tre humain, de rdiger selon les rgles dune grammaire donne et destins reprsenter les objets et les commandes pouvant entrer dans la constitution dun programme. Ni le langage machine, trop loign des modes dexpressions humains, ni les langues naturelles crites ou parles, trop ambigus, ne sont des langages de programmation. La production de logiciel est une activit difcile et complexe et les diteurs font payer, parfois trs cher, leur logiciel dont le code source nest, en gnral, pas distribu. Toutefois, tous les logiciels ne sont pas payants. La communaut internationale des informaticiens produit depuis longtemps du logiciel gratuit (ce qui ne veut pas dire quil est de mauvaise qualit, bien au contraire) mis la disposition de tous. Il existe aux tats-Unis, une fondation, la FSF (Free Software Foundation), linitiative de R. S TALLMAN, qui a pour but la promotion de la construction du logiciel libre, ainsi que celle de sa distribution. Libre ne veut pas dire ncessairement gratuit2 , bien que cela soit souvent le cas, mais indique que le texte source du logiciel est disponible. Dailleurs, la FSF propose une licence, GNU GPL, an de garantir que les logiciels sous cette licence soient libres dtre redistribus et modis par tous leurs utilisateurs.

1.3

LES LANGAGES DE PROGRAMMATION

Nous venons de voir que chaque ordinateur possde un langage qui lui est propre : le langage machine, qui est en gnral totalement incompatible avec celui dun ordinateur dun autre modle. Ainsi, un programme crit dans le langage dun ordinateur donn ne pourra tre rutilis sur un autre ordinateur. Le langage dassemblage est un codage alphanumrique du langage machine. Il est plus lisible que ce dernier et surtout permet un adressage relatif de la mmoire. Toutefois, comme le langage machine, le langage dassemblage est lui aussi dpendant dun ordinateur donn (voire dune famille dordinateurs) et ne facilite pas le transport des programmes vers des machines dont larchitecture est diffrente. Lexcution dun programme crit en langage dassemblage ncessite sa traduction pralable en langage machine par un programme spcial, lassembleur. Le texte qui suit, crit en langage dassemblage dun Core 2 dIntel, correspond lappel de la fonction C printf("Bonjour\n") qui crit Bonjour sur la sortie standard (e.g. lcran).
.LC0: .string "Bonjour" .text movl $.LC0, (%esp) call puts

Le langage dassemblage, comme le langage machine, est dun niveau trs lmentaire (une suite linaire de commandes et sans structure) et, comme le montre lexemple prcdent, gure lisible et comprhensible. Son utilisation par un tre humain est alors difcile, fastidieuse et sujette erreurs.
2 La

confusion provient du fait quen anglais le mot free possde les deux sens.

Chapitre 1 Introduction

Ces dfauts, entre autres, ont conduit la conception des langages de programmation dits de haut niveau. Un langage de programmation de haut niveau offrira au programmeur des moyens dexpression structurs proches des problmes rsoudre et qui amlioreront la abilit des programmes. Pendant de nombreuses annes, les ardents dfenseurs de la programmation en langage dassemblage avanaient le critre de son efcacit. Les optimiseurs de code ont balay cet argument depuis longtemps, et les dfauts de ces langages sont tels que leurs thurifraires sont de plus en plus rares. Si on ajoute un ordinateur un langage de programmation, tout se passe comme si lon disposait dun nouvel ordinateur (une machine abstraite), dont le langage est maintenant adapt ltre humain, aux problmes quil veut rsoudre et la faon quil a de comprendre et de raisonner. De plus, cet ordinateur ctif pourra recouvrir des ordinateurs diffrents, si le langage de programmation peut tre install sur chacun deux. Ce dernier point est trs important, puisquil signie quun programme crit dans un langage de haut niveau pourra tre exploit (thoriquement) sans modication sur des ordinateurs diffrents. La dnition dun langage de programmation recouvre trois aspects fondamentaux. Le premier, appel lexical, dnit les symboles (ou caractres) qui servent la rdaction des programmes et les rgles de formation des mots du langage. Par exemple, un entier dcimal sera dni comme une suite de chiffres compris entre 0 et 9. Le second, appel syntaxique, est lensemble des rgles grammaticales qui organisent les mots en phrases. Par exemple, la phrase 234 / 54 , forme de deux entiers et dun oprateur de division, suit la rgle grammaticale qui dcrit une expression. Le dernier aspect, appel smantique, tudie la signication des phrases. Il dnit les rgles qui donnent un sens aux phrases. Notez quune phrase peut tre syntaxiquement valide, mais incorrecte du point de vue de sa smantique (e.g. 234/0, une division par zro est invalide). Lensemble des rgles lexicales, syntaxiques et smantiques dnit un langage de programmation, et on dira quun programme appartient un langage de programmation donn sil vrie cet ensemble de rgles. La vrication de la conformit lexicale, syntaxique et smantique dun programme est assure automatiquement par des analyseurs qui sappuient, en gnral, sur des notations formelles qui dcrivent sans ambigut lensemble des rgles. Comment excuter un programme rdig dans un langage de programmation de haut niveau sur un ordinateur qui, nous le savons, ne sait traiter que des programmes crits dans son langage machine ? Voici deux grandes familles de mthodes qui permettent de rsoudre ce problme : La premire mthode consiste traduire le programme, appel source, crit dans le langage de haut niveau, en un programme smantiquement quivalent crit dans le langage machine de lordinateur (voir la gure 1.2). Cette traduction est faite au moyen dun logiciel spcialis appel compilateur. Un compilateur possde au moins quatre phases : trois phases danalyse (lexicale, syntaxique et smantique), et une phase de production de code machine. Bien sr, le compilateur ne produit le code machine que si le programme source respecte les rgles du langage, sinon il devra signaler les erreurs au moyen de messages prcis. En gnral, le compilateur produit du code pour un seul type de machine, celui sur lequel il est install. Notez que certains compilateurs, dits multi-cibles, produisent du code pour diffrentes familles dordinateurs. Nous avons vu quun langage de programmation dnit un ordinateur ctif. La seconde mthode consiste simuler le fonctionnement de cet ordinateur ctif sur lordinateur

1.3

Les langages de programmation

programme source

compilateur

donnes

langage machine

rsultats

F IG . 1.2 Traduction en langage machine.

rel par interprtation des instructions du langage de programmation de haut niveau. Le logiciel qui effectue cette interprtation sappelle un interprte. Linterprtation directe des instructions du langage est en gnral difcilement ralisable. Une premire phase de traduction du langage de haut niveau vers un langage intermdiaire de plus bas niveau est dabord effectue. Remarquez que cette phase de traduction comporte les mmes phases danalyse quun compilateur. Linterprtation est alors faite sur le langage intermdiaire. Cest la technique dimplantation du langage JAVA (voir la gure 1.3), mais aussi de beaucoup dautres langages. Un programme source JAVA est dabord traduit en un programme objet crit dans un langage intermdiaire, appel JAVA pseudocode (ou byte-code). Le programme objet est ensuite excut par la machine virtuelle JAVA, JVM (Java Virtual Machine).
programme source Java

compilateur

langage intermdiaire

interprte JVM

rsultats

donnes
F IG . 1.3 Traduction et interprtation dun programme J AVA.

Ces deux mthodes, compilation et interprtation, ne sont pas incompatibles, et bien souvent pour un mme langage les deux techniques sont mises en uvre. Lintrt de linterprtation est dassurer au langage, ainsi quaux programmes, une grande portabilit. Ils dpendent faiblement de leur environnement dimplantation et peu ou pas de modications sont ncessaires leur excution dans un environnement diffrent. Son inconvnient majeur est que le temps dexcution des programmes interprts est notablement plus important que celui des programmes compils.

Chapitre 1 Introduction

Bref historique La conception des langages de programmation a souvent t inuence par un domaine dapplication particulier, un type dordinateur disponible, ou les deux la fois. Depuis prs de cinquante ans, plusieurs centaines de langages de programmation ont t conus. Certains nexistent plus, dautres ont eu un usage limit, et seule une minorit sont vraiment trs utiliss. Le but de ce paragraphe est de donner quelques repres importants dans lhistoire des langages de programmation classiques . Il nest pas question de dresser ici un historique exhaustif. Le lecteur pourra se reporter avec intrt aux ouvrages [Sam69, Wex81, Hor83] qui retracent les vingt-cinq premires annes de cette histoire, [ACM93] pour les quinze annes qui suivirent, et [M+ 89] qui prsente un panorama complet des langages objets. F ORTRAN (Formula Translator) [Int57, ANS78] fut le premier traducteur en langage machine dune notation algbrique pour crire des formules mathmatiques. Il fut conu IBM partir de 1954 par J. BACKUS en collaboration avec dautres chercheurs. Jusqu cette date, les programmes taient crits en langage machine ou dassemblage, et limportance de F OR TRAN a t de faire la dmonstration, face au scepticisme de certains, de lefcacit de la traduction automatique dune notation volue pour la rdaction de programmes de calcul numrique scientique. lorigine, F ORTRAN nest pas un langage et ses auteurs nen imaginaient pas la conception. En revanche, il ont invent des techniques doptimisation de code particulirement efcaces. L ISP (List Processor) a t dvelopp partir de la n de lanne 1958 par J. M C C AR au MIT pour le traitement de donnes symboliques (i.e. non numriques) dans le domaine de lintelligence articielle. Il fut utilis pour rsoudre des problmes dintgration et de diffrenciation symboliques, de preuve de thormes, ou encore de gomtrie et a servi au dveloppement de modles thoriques de linformatique. La notation utilise, appele Sexpression, est plus proche dun langage dassemblage dune machine abstraite spcialise dans la manipulation de liste (le type de donne fondamental ; un programme L ISP est luimme une liste), que dun vritable langage de programmation. Cette notation prxe de type fonctionnel utilise les expressions conditionnelles et les fonctions rcursives bases sur la notation (lambda) de A. C HURCH. Une notation, appele M-expression, sinspirant de F ORTRAN et traduire en S-expression, avait t conue la n des annes 50 par J. M C C ARTHY, mais na jamais t implmente. Hormis L ISP 2, un langage inspir de A LGOL 60 (voir paragraphes suivants) dvelopp et implment au milieu des annes 60, les nombreuses versions et variantes ultrieures de L ISP seront bases sur la notation S-expression. Il est noter aussi que L ISP est le premier mettre en uvre un systme de rcupration automatique de mmoire (garbage-collector).
THY

La gestion est un autre domaine important de linformatique. Au cours des annes 50 furent dvelopps plusieurs langages de programmation spcialiss dans ce domaine. partir de 1959, un groupe de travail comprenant des universitaires, mais surtout des industriels amricains, sous lgide du Dpartement de la Dfense des tats-Unis (DOD), rchit la conception dun langage dapplications de gestion commun. Le langage C OBOL (Common Business Oriented Language) [Cob60] est le fruit de cette rexion. Il a pos les premires bases de la structuration des donnes. On peut dire que les annes 50 correspondent lapproche exprimentale de ltude des concepts des langages de programmation. Il est notable que F ORTRAN, L ISP et C OBOL, sous

1.3

Les langages de programmation

des formes qui ont bien volu, sont encore largement utiliss aujourdhui. Les annes 60 correspondent lapproche mathmatique de ces concepts, et le dveloppement de ce quon appelle la thorie des langages. En particulier, beaucoup de notations formelles sont apparues pour dcrire la smantique des langages de programmation. De tous les langages, A LGOL 60 (Algorithmic Language) [Nau60] est celui qui a eu le plus dinuence sur les autres. Cest le premier langage dni par un comit international (prsid par J. BACKUS et presque uniquement compos duniversitaires), le premier sparer les aspects lexicaux et syntaxiques, donner une dnition syntaxique formelle (la Forme Normale de BACKUS, BNF), et le premier soumettre la dnition lensemble de la communaut pour en permettre la rvision avant de ger quoi que ce soit. De nombreux concepts, que lon retrouvera dans la plupart des langages de programmation qui suivront, ont t dnis pour la premire fois dans A LGOL 60 (la structure de bloc, le concept de dclaration, le passage des paramtres, les procdures rcursives, les tableaux dynamiques, les noncs conditionnels et itratifs, le modle de pile dexcution, etc.). Pour toutes ces raisons, et malgr quelques lacunes mises en vidence par D. K NUTH [Knu67], A LGOL 60 est le langage qui t le plus progresser linformatique. Dans ces annes 60, des tentatives de dnition de langages universels, cest--dire pouvant sappliquer tous les domaines, ont vu le jour. Les langages PL/I (Programming Language One) [ANS76] et A LGOL 68 reprennent toutes les bonnes caractristiques de leurs ans conus dans les annes 50. PL/I cherche combiner en un seul langage C OBOL, L ISP, F ORTRAN (entre autres langages), alors quA LGOL 68 est le successeur ofciel dA LGOL 60. Ces langages, de part la trop grande complexit de leur dnition, et par consquence de leur utilisation, nont pas connu le succs attendu. Lui aussi fortement inspir par A LGOL 60, PASCAL [NAJN75, AFN82] est conu par N. W IRTH en 1969. Dune grande simplicit conceptuelle, ce langage algorithmique a servi (et peut-tre encore aujourdhui) pendant de nombreuses annes lenseignement de la programmation dans les universits. Le langage C [KR88, ANS89] a t dvelopp en 1972 par D. R ITCHIE pour la rcriture du systme dexploitation U NIX. Conu lorigine comme langage dcriture de systme, ce langage est utilis pour la programmation de toutes sortes dapplications. Malgr de nombreux dfauts, C est encore trs utilis aujourdhui, sans doute pour des raisons defcacit du code produit et une certaine portabilit des programmes. Ce langage a t normalis en 1989 par lANSI3 . Les annes 70 correspondent lapproche gnie logiciel . Devant le cot et la complexit toujours croissants des logiciels, il devient essentiel de dvelopper de nouveaux langages puissants, ainsi quune mthodologie pour guider la construction, matriser la complexit, et assurer la abilit des programmes. A LPHARD [W+ 76] et C LU [L+ 77], deux langages exprimentaux, M ODULA-2 [Wir85], ou encore A DA [ANS83] sont des exemples parmi dautres de langages imposant une mthodologie dans la conception des programmes. Une des originalits du langage A DA est certainement son mode de dnition. Il est le produit dun appel doffres international lanc en 1974 par le DOD pour unier la programmation de ses systmes embarqus. Suivirent de nombreuses annes dtude de conception pour dboucher sur une norme (ANSI, 1983), pose comme pralable lexploitation du langage.
3 American

National Standards Institute, linstitut de normalisation des tats-Unis.

10

Chapitre 1 Introduction

Les langages des annes 80-90, dans le domaine du gnie logiciel, mettent en avant le concept de la programmation objet. Cette notion nest pas nouvelle puisquelle date de la n des annes 60 avec S IMULA [DN66], certainement le premier langage objets. Toutefois, ce nest que rcemment quelle connat une certaine vogue. S MALLTALK [GR89], C++ [Str86] (issu de C), E IFFEL [Mey92], ou JAVA [GJS96, GR08], ou encore plus recemment C# [SG08] sont, parmi les trs nombreux langages objets, les plus connus. JAVA connat aujourdhui un grand engouement, en particulier grce au W EB dInternet. Ces quinze dernires annes bien dautres langages ont t conus autour de cette technologie. Citons, par exemple, les langages de script (langages de commandes conus pour tre interprts) JAVA S CRIPT [Fla10] destin la programmation ct client, et PHP [Mac10] dni pour la programmation ct serveur HTTP. Dans le domaine de lintelligence articielle, nous avons dj cit L ISP. Un autre langage, le langage dclaratif P ROLOG (Programmation en Logique) [CKvC83], conu ds 1972 par lquipe marseillaise de A. C OLMERAUER, a connu une grande notorit dans les annes 80. P ROLOG est issu de travaux sur le dialogue homme-machine en langage naturel et sur les dmonstrateurs automatiques de thormes. Un programme P ROLOG ne sappuie plus sur un algorithme, mais sur la dclaration dun ensemble de rgles partir desquelles les rsultats pourront tre dduits par unication et rtro-parcours (backtracking) laide dun valuateur spcialis. Poursuivons cet historique par le langage I CON [GHK79]. Il est le dernier dune famille de langages de manipulation de chanes de caractres (S NOBOL 1 4 et SL5) conus par R. G RISWOLD ds 1960 pour le traitement de donnes symboliques. Ces langages intgrent le mcanisme de confrontation de modles (pattern matching), la notion de succs et dchec de lvaluation dune expression, mais lide la plus originale introduite par I CON est celle du mcanisme de gnrateur et dvaluation dirige par le but. Un gnrateur est une expression qui peut fournir zro ou plusieurs rsultats, et lvaluation dirige par le but permet dexploiter les squences de rsultats produites par les gnrateurs. Ces langages ont connu un vif succs et il existe aujourdhui encore une grande activit autour du langage I CON. Si lide de langages universels des annes 60 a t aujourdhui abandonne, plusieurs langages dits multi-paradigme ont vu le jour ces dernires annes. Parmi eux, citons, pour terminer ce bref historique, les langages P YTHON [Lut09], RUBY [FM08] et S CALA [OSV08]. Les deux premiers langages sont typage dynamique (vrication de la cohrence des types de donnes lexcution) et incluent les paradigmes fonctionnel et objet. De plus, P YTHON intgre la notion de gnrateur similaire celle dI CON et RUBY permet la manipulation des processus lgers (threads) pour la programmation concurrente. S CALA, quant lui, intgre les paradigmes objet et fonctionnel avec un typage statique fort. La mise en uvre du langage permet la production de bytecode pour la machine virtuelle JVM, ce qui lui offre une grande compatibilit avec le langage JAVA.

1.4

CONSTRUCTION DES PROGRAMMES

Lactivit de programmation est difcile et complexe. Le but de tout programme est de calculer et retourner des rsultats valides et ables. Quelle que soit la taille des programmes, de quelques dizaines de lignes plusieurs centaines de milliers, la conception des programmes

1.4

Construction des programmes

11

exige des mthodes rigoureuses, si les objectifs de justesse et abilit veulent tre atteints. Dune faon trs gnrale, on peut dire quun programme effectue des actions sur des objets. Jusque dans les annes 60, la structuration des programmes ntait pas un souci majeur. Cest partir des annes 70, face des cots de dveloppement des logiciels croissants, que lintrt pour la structuration des programmes sest accrue. cette poque, les mthodes de construction des programmes commenaient par structurer les actions. La structuration des objets venait ultrieurement. Depuis la n des annes 80, le processus est invers. Essentiellement pour des raisons de prennit (relative) des objets par rapport celle des actions : les programmes sont structurs dabord autour des objets. Les choix de structuration des actions sont xs par la suite. Lorsque le choix des actions prcde celui des objets, le problme rsoudre est dcompos, en termes dactions, en sous-problmes plus simples, eux-mmes dcomposs en dautres sous-problmes encore plus simples, jusqu obtenir des lments directement programmables. Avec cette mthode de construction, souvent appele programmation descendante par rafnements successifs, la reprsentation particulire des objets, sur lesquels portent les actions, est retarde le plus possible. Lanalyse du problme traiter se fait dans le sens descendant dune arborescence, dont chaque nud correspond un sous-problme bien dtermin du programme construire. Au niveau de la racine de larbre, on trouve le problme pos dans sa forme initiale. Au niveau des feuilles, correspondent des actions pouvant snoncer directement et sans ambigut dans le langage de programmation choisi. Sur une mme branche, le passage du nud pre ses ls correspond un accroissement du niveau de dtail avec lequel est dcrite la partie correspondante. Notez que sur le plan horizontal, les diffrents sous-problmes doivent avoir chacun une cohrence propre et donc minimiser leur nombre de relations. En revanche, lorsque le choix des objets prcde celui des actions, la structure du programme est fonde sur les objets et sur leurs interactions. Le problme rsoudre est vu comme une modlisation (oprationnelle) dun aspect du monde rel constitu dobjets. Cette vision est particulirement vidente avec les logiciels graphiques et plus encore, de simulation. Les objets sont des composants qui contiennent des attributs (donnes) et des mthodes (actions) qui dcrivent le comportement de lobjet. La communication entre objets se fait par envoi de messages, qui donne laccs un attribut ou qui lance une mthode. Les critres de abilit et de validit ne sont pas les seuls caractriser la qualit dun programme. Il est frquent quun programme soit modi pour apporter de nouvelles fonctionnalits ou pour voluer dans des environnements diffrents, ou soit dpec pour fournir des pices dtaches dautres programmes. Ainsi de nouveaux critres de qualit, tels que lextensibilit, la compatibilit ou la rutilisabilit, viennent sajouter aux prcdents. Nous verrons que lapproche objet, bien plus que la mthode traditionnelle de dcomposition fonctionnelle, permet de mieux respecter ces critres de qualit. Les actions mises en jeu dans les deux mthodologies prcdentes reposent sur la notion dalgorithme4 . Lalgorithme dcrit, de faon non ambigu, lordonnancement des actions
4 Le mot algorithme ne vient pas comme certains le pensent, du mot logarithme, mais doit son origine un mathmaticien persan du IXe sicle, dont le nom abrg tait A L -K HOWRIZM (de la ville de Khowrizm). Cette ville situe dans lzbekist n, sappelle aujourdhui Khiva. Notez toutefois que cette notion est bien plus ancienne. Les a Babyloniens de lAntiquit, les gyptiens ou les Grecs avaient dj formul des rgles pour rsoudre des quations. Euclide (vers 300 av. J.C.) conut un algorithme permettant de trouver le pgcd de deux nombres.

12

Chapitre 1 Introduction

effectuer dans le temps pour spcier une fonctionnalit traiter de faon automatique. Il est dnot laide dune notation formelle, qui peut tre indpendante du langage utilis pour le programmer. La conception dalgorithme est une tche difcile qui ncessite une grande rexion. Notez que le travail requis pour lexprimer dans une notation particulire, cest--dire la programmation de lalgorithme dans un langage particulier, est rduit par comparaison celui de sa conception. La rexion sur papier, stylo en main, sera le pralable toute programmation sur ordinateur. Pour un mme problme, il existe bien souvent plusieurs algorithmes qui conduisent sa solution. Le choix du meilleur algorithme est alors gnralement guid par des critres defcacit. La complexit dun algorithme est une mesure thorique de ses performances en fonction dlments caractristiques de lalgorithme. Le mot thorique signie en particulier que la mesure est indpendante de lenvironnement matriel et logiciel. Nous verrons la section 10.5 page 109 comment tablir cette mesure. Le travail principal dans la conception dun programme rsidera dans le choix des objets qui le structureront, la validation de leurs interactions et le choix et la vrication des algorithmes sous-jacents.

1.5

DMONSTRATION DE VALIDIT

Notre but est de construire des programmes valides, cest--dire conformes ce que lon attend deux. Comment vrier la validit dun programme ? Une fois le programme crit, on peut, par exemple, tester son excution. Si la phase de test, cest--dire la vrication exprimentale par lexcution du programme sur des donnes particulires, est ncessaire, elle ne permet en aucun cas de dmontrer la justesse 100% du programme. En effet, il faudrait faire un test exhaustif sur lensemble des valeurs possibles des donnes. Ainsi, pour une simple addition de deux entiers cods sur 32 bits, soit 232 valeurs possibles par entier, il faudrait tester 2322 oprations. Pour une -seconde par opration, il faudrait 9109 annes ! N. W IRTH rsume cette ide dans [Wir75] par la formule suivante : Lexprimentation des programmes peut servir montrer la prsence derreurs, mais jamais prouver leur absence. La preuve5 de la validit dun programme ne pourra donc se faire que formellement de faon analytique, tout le long de la construction du programme et, videmment, pas une fois que celui-ci est termin. La technique que nous utiliserons est base sur des assertions qui dcriront les proprits des lments (objets, actions) du programme. Par exemple, une assertion indiquera quen tel point du programme telle valeur entire est ngative. Nous parlerons plus tard des assertions portant sur les objets. Celles pour dcrire les proprits des actions, cest--dire leur smantique, suivront laxiomatique de C.A.R. H OARE [Hoa69]. Lassertion qui prcde une action sappelle lantcdent ou pr-condition et celle qui la suit le consquent ou post-condition.
5 La

preuve de programme est un domaine de recherche thorique ancien, mais toujours ouvert et trs actif.

1.5

Dmonstration de validit

13

Pour chaque action du programme, il sera possible, grce des rgles de dduction, de dduire de faon systmatique le consquent partir de lantcdent. Notez quil est galement possible de dduire lantcdent partir du consquent. Ainsi pour une tche particulire, forme par un enchanement dactions, nous pourrons dmontrer son exactitude, cest--dire le passage de lantcdent initial jusquau consquent nal, par application des rgles de dduction sur toutes les actions qui le composent. Il est trs important de comprendre, que les afrmations dun programme ne doivent pas tre dnies a posteriori, cest--dire une fois le programme crit, mais bien au contraire a priori puisquil sagit de construire laction en fonction de leffet prvu. Une action A avec son antcdent et son consquent sera dnote :
{antcdent} A {consquent}

Les assertions doivent tre les plus formelles possibles, si lon dsire prouver la validit du programme. Elles sapparentent dailleurs la notion mathmatique de prdicat. Toutefois, il sera ncessaire de trouver un compromis entre leur complexit et celle du programme. En dautres termes, sil est plus difcile de construire ces assertions que le programme lui-mme, on peut se demander quel est leur intrt ? Certains langages de programmation, en fait un nombre rduit6 , intgrent des mcanismes de vrication de la validit des assertions spcies par les programmeurs. Dans ces langages, les assertions font donc parties intgrantes du programme. Elles sont contrles au fur et mesure de lexcution du programme, ce qui permet de dtecter une situation derreur. En JAVA, une assertion est reprsente par une expression boolenne introduite par lnonc assert. Le caractre boolen de lassertion est toutefois assez rducteur car bien souvent les programmes doivent utiliser des assertions avec les quanticateurs de la logique du premier ordre que cet nonc ne pourra exprimer. Des extensions au langage laide dannotations spciales, comme par exemple [LC06], ont t rcemment proposes pour obtenir une spcication formelle des programmes JAVA. Dans les autres langages, les assertions, mme si elles ne sont pas traites automatiquement par le systme, devront tre exprimes sous forme de commentaires. Ces commentaires serviront lauteur du programme, ou aux lecteurs, se convaincre de la validit du programme.

6 Citons certains langages exprimentaux conus dans les annes 70, tels que A LPHARD [M. 81], ou plus rcemment E IFFEL.

Chapitre 2

Actions lmentaires

Un programme est un processus de calcul qui peut tre modlis de diffrentes faons. Nous considrons tout dabord quun programme est une suite de commandes qui effectuent des actions sur des donnes appeles objets, et quil peut tre dcrit par une fonction f dont lensemble de dpart D est un ensemble de donnes, et lensemble darrive R est un ensemble de rsultats : f :DR ce schma, on peut faire correspondre trois premires actions lmentaires, ou noncs simples, que sont la lecture dune donne, lexcution dune procdure ou dune fonction prdnie sur cette donne et lcriture dun rsultat.

2.1

LECTURE DUNE DONNE

La lecture dune donne consiste faire entrer un objet en mmoire centrale partir dun quipement externe. Selon le cas, cette action peut prciser lquipement sur lequel lobjet doit tre lu, et o il se situe sur cet quipement. La faon dexprimer lordre de lecture varie bien videmment dun langage un autre. Pour linstant, nous nous occuperons uniquement de lire des donnes au clavier, cest--dire sur lentre standard et nous appellerons lire laction de lecture. Une fois lu, lobjet plac en mmoire doit porter un nom, permettant de le distinguer sans ambigut des objets dj prsents. Ce nom sera cit chaque fois quon utilisera lobjet en question dans la suite du programme. Cest laction de lecture qui prcise le nom de lobjet

16

Chapitre 2 Actions lmentaires

lu. La lecture dun objet sur lentre standard placer en mmoire centrale sous le nom x scrit de la faon suivante :
{il existe une donne lire sur lentre standard} lire(x) {une donne a t lue sur lentre standard, place en mmoire centrale et le nom x permet de la dsigner}

Notez que plusieurs commandes de lecture peuvent tre excutes les unes la suite des autres. Si le mme nom est utilis chaque fois, il dsignera la dernire donne lue.

2.2

EXCUTION DUNE PROCDURE PRDFINIE

Lobjet qui vient dtre lu et plac en mmoire peut tre la donne dun calcul, et en particulier la donne dune procdure ou dune fonction prdnie. On dit alors que lobjet est un paramtre effectif donne de la procdure ou de la fonction. Ces procdures ou ces fonctions sont souvent conserves dans des bibliothques et sont directement accessibles par le programme. Traditionnellement, les langages de programmation proposent des fonctions mathmatiques et des procdures dentres-sorties. Lexcution dune procdure ou dune fonction est une action lmentaire qui correspond ce quon nomme un appel de procdure ou de fonction. Par exemple, la notation sin(x) est un appel de fonction qui calcule le sinus de x, o x dsigne le nom de lobjet en mmoire. Une fois lappel dune procdure ou dune fonction effectu, comment rcuprer le rsultat du calcul ? Sil sagit dune fonction f, la notation f(x) sert la fois commander lappel et nommer le rsultat. Cest la notion de fonction des mathmaticiens. Ainsi, sin(x) est la fois lappel de la fonction et le rsultat.
{le nom x dsigne une valeur en mmoire} sin(x) {lappel de sin(x) a calcul le sinus de x}

Bien videmment, il est possible de fournir plusieurs paramtres donne lors de lappel dune fonction. Par exemple, la notation f(x,y,z) correspond lappel dune fonction f avec trois donnes nommes respectivement x, y et z. Il peut tre galement utile de donner un nom au rsultat. Avec une procdure, il sera possible de prciser ce nom au moment de lappel, sous la forme dun second paramtre, appel paramtre effectif rsultat .
{le nom x dsigne une valeur en mmoire} P(x,y) {lappel de la procdure P sur la donne x} {a calcul un rsultat dsign par y}

Comme une fonction, une procdure peut possder plusieurs paramtres donne . De plus, si elle produit plusieurs rsultats, ils sont dsigns par plusieurs paramtres rsultat .

2.3

criture dun rsultat

17

{les noms x et y dsignent des valeurs en mmoire} P(x,y,a,b,c) {lappel de la procdure P sur les donnes x et y a calcul trois rsultats dsigns par a, b, c}

Remarquez que rien dans la notation de cet appel de procdure ne distinguent les paramtres effectifs donne des paramtres effectifs rsultat . Nous verrons au chapitre 6 comme sopre cette distinction. Notez galement que puisquune fonction ne produit quun seul rsultat donn par la dnotation de lappel, une fonction ne doit pas possder de paramtre rsultat . Certains langages de programmation en font une rgle, mais malheureusement, bien souvent, ils autorisent les fonctions possder des paramtres rsultat .

2.3

CRITURE DUN RSULTAT

Une fois le rsultat dune procdure ou dune fonction calcul, il est souvent souhaitable de rcuprer ce rsultat sur un quipement externe. Il existe pour cela une action lmentaire rciproque de celle de lecture. Cest laction dcriture. Elle consiste transfrer vers un quipement externe dsign, la valeur dun objet en mmoire. Une transcodication est associe cette action dans le cas o le destinataire nal est un tre humain. Pour linstant, nous crirons les rsultats sur lcran, quon nomme la sortie standard.
{le nom y dsigne une valeur en mmoire} crire(y) {la valeur de y a t crite sur la sortie standard}

Notez que les actions de lecture et dcriture correspondent des appels de procdure et que les paramtres effectifs de ces deux procdures sont de type donne .

2.4

AFFECTATION DUN NOM UN OBJET

Nous avons vu quil tait possible de donner un nom un objet particulier, soit par une action de lecture, soit par lintermdiaire dun paramtre rsultat dune procdure. Est-ce que la relation tablie entre un nom et lobjet quil dsigne reste vrie tout au long de lexcution du programme ? Cela dpend du programme. Cette relation peut rester vrie durant toute lexcution du programme, mais aussi cesser. Considrons les deux lectures conscutives suivantes :
lire(x) lire(x)

Aprs la seconde lecture, la premire relation entre x et lobjet lu a cess, et une seconde a t tablie entre le mme nom x et le second objet lu sur lentre standard. Un nom qui sert dsigner un ou plusieurs objets sappelle une variable. Il est pourtant utile ou ncessaire, en particulier pour des raisons de abilit du programme, de garantir quun nom dsigne toujours le mme objet en tout point du programme. Un nom

18

Chapitre 2 Actions lmentaires

qui ne peut dsigner quun seul objet, cest--dire que la relation qui les lie ne peut tre remise en cause, sappelle une constante. Y-a-t-il dautres faons dtablir cette relation que les deux que nous venons dindiquer ? La rponse est afrmative. Presque tous les langages de programmation possdent une action lmentaire, appele affectation, qui associe un nom un objet. Chaque langage de programmation a sa manire de concevoir et de reprsenter laction daffectation, mais cette action comporte toujours trois parties : le nom choisi, lobjet dsigner et le signe opratoire identiant laction daffecter. Dans un langage comme PASCAL, le signe daffectation est :=. Lexemple suivant montre deux actions daffectation conscutives :
x:=6; y:=x

Il faut bien comprendre que y:=x signie faire dsigner par y le mme objet que celui dsign par x , en loccurrence 6, et non pas faire que les noms x et y soient les mmes . Dans notre notation algorithmique, nous choisirons le signe opratoire pour reprsenter laffectation.

2.5

DCLARATION DUN NOM

Pour des raisons de scurit des programmes construits, certains langages de programmation exigent que les noms qui servent dsigner les objets soient dclars. Cest le cas par exemple en JAVA o tous les noms (non prdnis) doivent avoir t dclars au pralable laide de commandes de dclaration. Toutefois, les noms prdnis peuvent tre utiliss tels quels sans dclaration pralable. An daccrotre la lisibilit des programmes (mme pour des programmes de petite taille), les noms choisis doivent tre signicatifs (et certainement longs), cest--dire quils possdent un sens qui exprime clairement leur utilisation ultrieure. Si ncessaire, un commentaire peut accompagner la dclaration pour donner plus de prcision. Notez toutefois que dans le cas de noms conventionnels, une seule lettre peut sufre. Par exemple, on notera a, b et c les trois coefcients dune quation du second degr, et souvent i lindice dune boucle (voir le chapitre 8).

2.5.1

Dclaration de constantes

Une dclaration de constante tablit un lien dnitif entre un nom et une valeur particulire. Ce nom sera appel identicateur de constante. Lexemple qui suit prsente la dclaration de deux constantes :
constantes nblettres = 26 {nombre de lettres dans lalphabet latin} nbtours = 33 {nombre de tours par minute}

2.6

Rgles de dduction

19

2.5.2

Dclaration de variables

Une dclaration de variable tablit un lien entre un nom et un ensemble de valeurs. Le nom ne pourra dsigner que des valeurs prises dans cet ensemble. Ce nom sappelle un identicateur de variable et lensemble de valeurs un type. Cette dernire notion sera prsente dans le chapitre suivant. Dans la dclaration de variables qui suit, le domaine de valeur de la variable rponse (introduit par le mot type, voir le chapitre 3) est un ensemble de caractres, alors que celui des variables racine1 et racine2 est un ensemble de rels.
variables rponse type caractre racine1, racine2 type rel

2.6

RGLES DE DDUCTION

Lappel de procdure et laffectation sont les deux premires actions lmentaires dont nous allons dnir les rgles de dduction. Pour vrier la validit de nos programmes, il nous faut donner les rgles de dduction de ces deux actions, cest--dire pouvoir dterminer le consquent en fonction de lantcdent par application de laction daffectation ou dappel de procdure.

2.6.1

Laffectation

Pour une affectation x e, la rgle de passage de lantcdent au consquent sexprime de la faon suivante :
{ A } x e { Ax } e

Ce qui peut se traduire par : dans lantcdent A remplacez toutes les apparitions libres1 de e par x. Par exemple, supposons quune variable i soit gale 10, que vaut i aprs laffectation i i+1 ? De toute vidence 11. Montrons-le en faisant apparatre la partie droite de laffectation dans lantcdent, puis en appliquant la rgle de dduction :
{i = {i + i {i = 10} 1 = 10 + 1 = 11} i + 1 11}

Rciproquement, on dduit lantcdent du consquent en remplaant dans le consquent toutes les apparitions de x par e.
{ Ax } x e e { A }

Dans lexemple suivant, il faut lire de bas en haut partir du consquent.


{x + y = 10} z x + y {z = 10}
1 Lexpression

e est sans effet de bord, cest--dire quelle ne modie pas son environnement.

20

Chapitre 2 Actions lmentaires

Considrons maintenant les quatre affectations suivantes :


d y d y

d y d y

+ + + +

2 d 2 d

Que calcule cette srie daffectations, lorsque lantcdent initial est gal :
{y = x2 , d = 2x - 1}

Lapplication des rgles de dduction montre que la variable y prend successivement les valeurs x2 , (x+1)2 et (x+2)2 . Notez quen poursuivant, on calcule la suite (x+i)2 .
{y = x2 , d = 2x - 1} d d + 2 { y + d = (x + 1)2 , d y y + d {y = (x + 1)2 , d = 2x d d + 2 {y + d = (x + 2)2 ), d y y + d {y = (x + 2)2 , d = 2x

= 2x + 1} + 1} = 2x + 3} + 3}

Tel quil est trait, cet exemple applique les rgles a posteriori. Rappelons, mme si cela est difcile, que les afrmations doivent tre construites a priori, ou du moins simultanment avec le programme.

2.6.2

Lappel de procdure

Les rgles de passage de lantcdent au consquent (et rciproquement) dpendent des paramtres et du rle de la procdure. Nous verrons comment dcrire ces rgles plus prcisment dans le chapitre 6.

2.7

LE PROGRAMME SINUS CRIT EN JAVA

Comment scrit en JAVA, le programme qui lit un entier sur lentre standard, qui calcule et crit son sinus sur la sortie standard ? Rappelons tout dabord lalgorithme.
Algorithme Sinus variable x type entier {il existe un entier lire sur lentre standard} lire(x) {un entier a t lu sur lentre standard, plac en mmoire centrale et le nom x permet de le dsigner} crire(sin(x)) {la valeur du sinus de x est crite sur la sortie standard}

2.7

Le programme sinus crit en Java

21

La programmation en JAVA de cet algorithme est la suivante :


/** La classe Sinus calcule et affiche sur la sortie standard le sinus dun entier lu sur lentre standard */ import java.io.*; class Sinus { public static void main (String[] args) throws IOException { int x; // il existe un entier lire sur lentre standard x = StdInput.readlnInt(); // un entier a t lu sur lentre standard, // plac en mmoire centrale et // lidentificateur de variable x permet de le dsigner System.out.println(Math.sin(x)); // la valeur du sinus de x est crite sur la sortie standard } } // fin classe Sinus

Ce premier programme comporte un certain nombre de choses mystrieuses et qui le resteront encore un peu en attendant la lecture des prochains chapitres. Toutefois, sachez ds prsent, que la structuration dun programme JAVA est faite autour des objets. Un programme JAVA est une collection de classes (voir plus loin le chapitre 7), place dans des chiers de texte, qui dcrit des objets manipuls lors de son excution. Il doit possder au moins une classe, ici Sinus, contenant la procdure main par laquelle dbutera lexcution du programme. Ici, cette classe sera conserve dans un chier qui porte le nom de la classe sufx par java, i.e. Sinus.java. Par convention, la premire lettre de chaque mot qui forme le nom de la classe est en majuscule. Ce premier programme comporte en tte un commentaire qui explique de faon concise son rle. Cest une bonne habitude de programmation que de mettre systmatiquement une telle information, qui pourra tre complte par le ou les noms des auteurs et la date de cration du programme. Le corps de la procdure main, plac entre deux accolades, dclare en premier lieu, la variable entire x. Les variables sont dclares en tte de procdure sans mot-cl particulier pour les introduire. Remarquez que le nom du type prcde le nom de la variable. Si le mot-cl final prcde la dclaration, alors il sagit dune dnition de constante (notez quaucune constante nest dclare dans ce premier programme). Par exemple, si nous devions dclarer la constante relle pi, nous pourrions crire :
final double pi = 3.1415926;

La constante prend une valeur lors de sa dclaration et ne pourra videmment plus tre modie par la suite. La lecture de lentier est faite grce la fonction readlnInt2 , qui lit sur lentre standard une suite de chiffres, sous forme de caractres, et renvoie la valeur du nombre entier quelle reprsente. Cet entier est ensuite affect la variable x. Vous noterez que le symbole daffec2 Cette

fonction nappartient pas lenvironnement standard de JAVA (voir la section 14.6 page 159).

22

Chapitre 2 Actions lmentaires

tation est le signe =. Attention de ne pas le confondre avec loprateur dgalit reprsent en JAVA par le symbole ==3 ! Le dernier nonc calcule et crit le sinus de x grce, respectivement, la fonction mathmatique sin et la procdure println. Les afrmations sont dnotes sous forme de commentaires introduits par deux barres obliques //. Remarquez la diffrence de notation avec le premier commentaire en tte de programme. Le langage JAVA propose trois formes de commentaires. Nous distinguerons les commentaires destins au programmeur de la classe et ceux destins lutilisateur de la classe. Les premiers dbutent par // et sachvent la n de la ligne, ou peuvent tre rdigs sur plusieurs lignes entre les dlimiteurs /* et */. Ils dcrivent en particulier les afrmations qui dmontrent la validit du programme. Les seconds, destins aux utilisateurs de la classe, sont appels commentaires de documentation. Ils apparaissent entre /** et */ et peuvent tre traits automatiquement par un outil, javadoc, pour produire la documentation du programme au format HTML (Hyper Text Markup Language).

2.8

EXERCICES

Exercice 2.1. Modiez le programme pour rendre lutilisation de la variable x inutile. Exercice 2.2. En partant de lantcdent {fact = i !}, appliquez la rgle de dduction de lnonc daffectation pour trouver le consquent des deux instructions suivantes :
i i+1 fact fact*i

3 Ce choix du symbole mathmatique dgalit pour laffectation est une aberration hrite du langage C [ANS89].

Chapitre 3

Types lmentaires

Une faon de distinguer les objets est de les classer en fonction des actions quon peut leur appliquer. Les classes obtenues en rpertoriant les diffrentes actions possibles, et en mettant dans la mme classe les objets qui peuvent tre soumis aux mmes actions sappellent des types. Classiquement, on distingue deux catgories de type : les types lmentaires et les types structurs. Dans ce chapitre, nous ntudierons que les objets lmentaires. On dira quun type est lmentaire, ou de type simple, si les actions qui le manipulent ne peuvent accder lobjet que dans sa totalit. Le plus souvent, les types lmentaires sont prdnis par le langage, cest--dire quils prexistent, et sont directement utilisables par le programmeur. Il sagit, par exemple, des types entier, rel, boolen ou caractre. Le programmeur peut galement dnir ses propres types lmentaires, en particulier pour spcier un domaine de valeur particulier. Certains langages de programmation offrent pour cela des constructeurs de types lmentaires. Un langage est dit typ si les variables sont associes un type particulier lors de leur dclaration. Pour ces langages, les compilateurs peuvent alors vrier la cohrence des types des variables, et ainsi garantir une plus grande abilit des programmes construits. Au contraire, les variables des langages de programmation non typs peuvent dsigner des objets de nimporte quel type et les vrications de cohrence de type sont reportes au moment de lexcution du programme. La programmation avec ces langages est moins sre, mais offre plus de souplesse. Dans ce chapitre, nous prsenterons les types lmentaires entier, rel, boolen et caractre, ainsi que les constructeurs de type numr et intervalle.

24

Chapitre 3 Types lmentaires

3.1

LE TYPE ENTIER

Le type entier reprsente partiellement lensemble des entiers relatifs Z des mathmaticiens. Alors que lensemble Z est inni, lensemble des valeurs dni par le type entier est ni, et limit par les possibilits de chaque ordinateur, en fait, par le nombre de bits utiliss pour sa reprsentation. Le type entier possde donc un lment minimum et un lment maximum. Chaque entier possde une reprsentation distincte sur lordinateur et la notation des constantes entires est en gnral classique : une suite de chiffres en base 10. La cardinalit du type entier dpend de la reprsentation binaire des nombres. Un mot de n bits permet de reprsenter 2n nombres positifs sur lintervalle [0, 2n 1]. Rciproquement, le nombre de bits ncessaires la reprsentation dun entier n est log2 n. An de simplier les oprations daddition et de soustraction, les entiers ngatifs sont reprsents sous forme complmente, soit en complment un , soit en complment deux. En complment un, la valeur ngative dun entier x est obtenue en inversant chaque position binaire de sa reprsentation. Par exemple, sur 4 bits lentier 6 est reprsent par 0110 et lentier 6 par la conguration binaire 1001. On obtient le complment deux, en ajoutant 1 au complment un. Lentier 6 est donc reprsent par 1010. En complment un, lensemble des entiers est dni par lintervalle [2n1 1, 2n1 1], o n est le nombre de bits utiliss pour reprsenter un entier. Notez quen complment un, lentier zro possde deux reprsentations binaires, la premire avec tous les bits 0 et la seconde avec tous les bits 1. En complment deux, le type entier est dni par lintervalle [2n1 , 2n1 1] et il nexiste quune seule reprsentation du zro. Une conguration binaire dont tous les bits valent un reprsente lentier 1. Les oprations de larithmtique classique sappliquent sur le type entier, de mme que les oprations de comparaison. Notez que les axiomes ordinaires de larithmtique entire ne sont pas valables sur lordinateur car ils ne sont pas vris quand on sort du domaine de dnition des entiers. En particulier, laddition nest pas une loi associative sur le type entier. Si entmax est lentier maximum et x un entier positif, la somme (entmax + 1) x nest pas dnie, alors que entmax + (1 x) appartient au domaine de dnition. Les types entiers de J AVA Tout dabord, notons quil nexiste pas un, mais quatre types entiers. Les types entiers
byte, short, int, long sont signs et reprsents en complment 2. Ils se distinguent par leur cardinal. Plus prcisment, les entiers du type byte sont reprsents sur 8 bits, ceux du type short sur 16 bits, ceux du type int sur 32 bits et ceux du type long sur 64 bits.

Pour chacun de ces quatre types, il existe deux constantes qui reprsentent lentier minimum et lentier maximum. Ces constantes sont donnes par la table 3.1. Les constantes entires en base 10 sont dnotes par une suite de chiffres compris entre
0 et 9. Le langage permet galement dexprimer des valeurs en base 8 ou 16, en les faisant prcder, respectivement, par les prxes 0 et 0x.
{exemples de constantes entires} 3 125 0 0777 3456 234 0xAC12

3.2

Le type rel

25

type byte short int long

minimum Byte.MIN_VALUE Short.MIN_VALUE Integer.MIN_VALUE Long.MIN_VALUE

maximum Byte.MAX_VALUE Short.MAX_VALUE Integer.MAX_VALUE Long.MAX_VALUE

TAB . 3.1 Valeurs minimales et maximales des types entiers de J AVA.

La table 3.2 montre les oprateurs arithmtiques et relationnels qui peuvent tre appliqus sur les types entiers de JAVA.
oprateur oprateurs arithmtiques
+ * / %

fonction oppos addition soustraction multiplication division modulo infrieur infrieur ou gal gal diffrent suprieur suprieur ou gal

exemple
-45 45 + 5 a - 4 a * b 5 / 45 a % 4 a < b a <= b a == b a != b a > b a >= b

oprateurs relationnels
< <= == != > >=

TAB . 3.2 Oprateurs sur les types entiers.

La dclaration dune variable entire est prcde du domaine de valeur particulier quelle peut prendre. Notez que plusieurs variables dun mme type peuvent tre dclares en mme temps.
byte unOctect, uneNote; short nbMots; int population; long nbtoiles, infini;

3.2

LE TYPE REL

Le type rel sert dnir partiellement lensemble R des mathmaticiens. Les rels ne peuvent tre reprsents sur lordinateur que par des approximations plus ou moins dles. Le type rel dcrit un nombre ni de reprsentants dintervalles du continuum des rels. Si deux objets rels sont dans le mme intervalle, ils auront le mme reprsentant et ne pourront pas tre distingus. De plus, les rels ne sont pas uniformment rpartis sur lensemble ; plus de la moiti est concentre sur lintervalle [1, 1]. Notez que le type entier nest pas inclus dans le type rel. Ils forment deux ensembles disjoints. La dnotation dune constante relle varie dun langage de programmation lautre et nous verrons plus loin le cas particulier de JAVA.

26

Chapitre 3 Types lmentaires

Larithmtique sur les rels est inexacte. Chaque opration conduit des rsultats approchs qui, rpte plusieurs fois, peut conduire des rsultats totalement faux. Les rsultats dpendent en effet de la reprsentation du nombre rel sur lordinateur, de la prcision avec laquelle il est obtenu ainsi que de la mthode utilise pour les calculer. Il existe plusieurs modes de reprsentation des rels. La plus courante est celle dite en virgule ottante. Un nombre rel x est reprsent par un triplet dentiers (s, e, m), tel que x = (1)s m B e , avec s {0, 1}, E < e < E et M < m < M . La valeur de s donne le signe du rel, m sappelle la mantisse, e lexposant et B la base (le plus souvent 2, 8 ou 16). E, M et B constituent les caractristiques de la reprsentation choisie, et dpendent de lordinateur. Limprcision de la reprsentation provient des valeurs limites E et M . De plus, les langages de programmation raisonnent en termes de nombres en base 10, alors que ces nombres sont reprsents dans des bases diffrentes. Les oprateurs darithmtique relle et de relation sont applicables sur les rels, mais il faut tenir compte du fait quelles peuvent conduire des rsultats faux. En particulier, le test de lgalit de deux nombres rels est bannir. Comme pour le type entier, ces oprations ne sont pas des lois de composition internes. La reprsentation dun nombre en virgule ottante nest pas unique tant que lemplacement du dlimiteur dans la mantisse nest pas dni, puisquun dcalage du dlimiteur peut tre compens par une modication de lexposant. Ainsi, 125.32 peut scrire 0.12532 103 , 1.2532 102 ou encore 12.532 101 . Pour lever cette ambigut, on adopte gnralement une reprsentation normalise1 . Les rels sont tels que la valeur de lexposant est ajuste pour que la mantisse ait le plus de chiffres signicatifs possibles. Aprs chaque opration, le rsultat obtenu est normalis. Les exemples suivants mettent en vidence linexactitude des calculs rels. Pour simplier, on considre que les nombres rels sont reprsents en base 10 et que la mantisse ne peut utiliser que quatre chiffres (M = 1000). Soient les rels x, y et z suivants : x = 9.900, y = 1.000, z = 0.999 on veut calculer (x + y) + z et x + (y + z). Les erreurs de calculs viennent des ajustements de reprsentation. Par exemple, lors dune addition de deux entiers, on ajuste la reprsentation du nombre qui a le plus petit exposant en valeur absolue de faon que les deux exposants soient gaux. On note x la reprsentation informatique de x. x = 9900 103 , y = 1000 103 , z = 9990 104 x + y = 10900 103 = 1090 102 ( + y ) + z = 1090 102 + 99 102 = 991 102 = 9910 103 x y + z = 1000 103 + 9990 104 = 1000 103 + 999 103 = 1 103 x + ( + z ) = 9900 103 + 1 103 = 9901 103 y Seul le calcul x + ( + z ) est juste. Lerreur, dans le calcul de ( + y ) + z , provient de la y x perte dun chiffre signicatif de z dans lajustement 2.
1 La norme IEEE 754 propose une reprsentation normalise des rels sur 32, 64, et 128 bits. Cette norme est aujourdhui trs utilise.

3.2

Le type rel

27

Considrons maintenant les trois rels x = 1100, y = 5, z = 5.001. On dsire calculer x ( + z ) et ( y ) + ( z ). y x x x = 1100 100 , y = 5000 103 , z = 5001 103 x y = 5500 100 x z = 5501 100 ( y ) + ( z ) = 1000 103 = 1 x x y + z = 1000 106 x ( + z ) = 1100 103 = 1.1 y L encore, seul le deuxime calcul est juste. Enn, traitons la rsolution dune quation du second degr avec les trois coefcients rels a = 1, b = 200, c = 1. Nous obtenons : a = 1000 103 , = 2000 101 , c = 1000 103 b = 4000 101 4000 103 = 2000 101

Dans le calcul de , la disparition de 4000 103 au cours de lajustement conduit au calcul dune racine fausse. On trouve x1 = 0 et x2 = 2000 101, alors que la racine x1 est normalement gale 0.005. Dans la ralit, la mantisse est beaucoup plus grande et les calculs plus prcis, mais les problmes restent identiques. Dune faon gnrale, les oprations daddition et de soustraction sont dangereuses lorsque les oprandes ont des valeurs voisines qui conduisent un rsultat proche de zro, de mme la division, lorsque le dnominateur est proche de zro. Les types rels de J AVA Comme pour les entiers, JAVA dnit plusieurs types rels : float et double. Les rels du type float sont en simple prcision cods sur 32 bits, ceux du type double sont en double prcision cods sur 64 bits. La reprsentation de ces rels suit la norme IEEE 754. Ces deux types possdent chacun une valeur minimale et une valeur maximale donnes par le tableau 3.3.
type float double minimum Float.MIN_VALUE Double.MIN_VALUE maximum Float.MAX_VALUE Double.MAX_VALUE

TAB . 3.3 Valeurs minimales et maximales des types rels de J AVA.

La dnotation dune constante relle suit la syntaxe donne ci-dessous. Les crochets indiquent que largument quils entourent est optionnel, la barre verticale un choix, et entier est un nombre entier en base 10.
entier[.][entier][[e|E][]entier] [entier][.]entier[[e|E][]entier]

Ainsi, les constantes suivantes sont valides :

28

Chapitre 3 Types lmentaires

123.456

34.0

.0

12.

56e34

4E-5 45.67e2

4567

La table 3.4 donne les oprateurs arithmtiques et relationnels applicables sur les types rels de JAVA.
oprateur oprateurs arithmtiques
+ * /

fonction oppos addition soustraction multiplication division infrieur infrieur ou gal gal diffrent suprieur suprieur ou gal

exemple
-45.5+5e12 45.5+5e12 a - 4.3 a * b 5 / 45 a < b a <= b a == b a != b a > b a >= b

oprateurs relationnels
< <= == != > >=

TAB . 3.4 Oprateurs sur les types rels.

Enn, les dclarations de variables de type rel ont la forme suivante :


float distance, rayon; double surface;

3.3

LE TYPE BOOLEN

Le type boolen est un type ni qui reprsente un ensemble compos de deux valeurs logiques, vrai et faux, sur lequel les oprations de disjonction (ou), de disjonction exclusive (xou), de conjonction (et), et de ngation (non) peuvent tre appliques. Ces oprations, que lon trouve dans la plupart des langages de programmation, sont entirement dnies au moyen de la table 3.5, dite de vrit.
p faux vrai faux vrai q faux faux vrai vrai non p vrai faux vrai faux p et q faux faux faux vrai p ou q faux vrai vrai vrai p xou q faux vrai vrai faux

TAB . 3.5 Table de vrit.

partir de cette table, on peut dduire un certain nombre de relations, et en particulier celles de D E M ORGAN quil est frquent dappliquer : non (p ou q) = non p et non q non (p et q) = non p ou non q

3.4

Le type caractre

29

Notez quun seul bit est ncessaire pour la reprsentation dune valeur boolenne. Mais, le plus souvent, un boolen est cod sur un octet avec, par convention, 0 pour la valeur faux et 1 pour la valeur vrai. Le type boolen de J AVA Le type boolean dnit les deux valeurs true et false. En plus des oprateurs de ngation (!), de disjonction (|), de disjonction exclusive (^) et de conjonction (&), JAVA propose les oprateurs de disjonction et de conjonction conditionnelle reprsents par les symboles || et &&. Le rsultat de p || q est vrai si p est vrai quelle que soit la valeur de q qui, dans ce cas, nest pas value. De mme, le rsultat de p && q est faux si p est faux quelle que soit la valeur de q qui, dans ce cas, nest pas value. Nous verrons ultrieurement que lutilisation de ces oprateurs a une inuence considrable sur le style de programmation. La dclaration suivante est un exemple de dclaration de variables boolennes.
boolean prsent, onContinue;

3.4

LE TYPE CARACTRE

Chaque ordinateur possde son propre jeu de caractres. La plupart des ordinateurs actuels proposent plusieurs jeux de caractres diffrents et normaliss pour reprsenter des lettres et des chiffres de diverses langues, des symboles graphiques ou des caractres de contrle. Par le pass, seuls deux jeux de caractres amricains taient vraiment disponibles. Le jeu de caractres A SCII2 , cod sur 7 bits, ne permet de reprsenter que 128 caractres diffrents. Le jeu E BCDIC3 , spcique IBM, code les caractres sur 8 bits et inclut quelques lettres trangres, comme par exemple, ou . Pour satisfaire les usagers non anglophones, la norme ISO-8859 propose plusieurs jeux de 256 caractres cods sur 8 bits. Les 128 premiers caractres sont ceux du jeu A SCII et les 128 suivants correspondent des variantes nationales. La norme ISO 8859-1, appele Latin-1, correspond la variante des pays de lEurope de lOuest. Elle inclut des symboles graphiques ainsi que des caractres signes diacritiques comme , , ou encore , qui existent la fois sous forme minuscule et majuscule (sauf et ). Notez que la norme ISO 8859-15, appele aussi Latin-9, est identique ISO 8859-1, lexception de huit nouveaux caractres dont le symbole de leuro e, Mais, face linternationalisation toujours croissante de linformatique, ces jeux de caractres ne sufsent plus pour reprsenter tous les symboles des diffrentes langues du monde. De plus, leurs diffrences sont un frein la portabilit des logiciels et des donnes. Depuis le dbut des annes 90, le Consortium Unicode, dveloppe une norme U NICODE pour dnir un systme de codage universel pour tous les systmes dcritures. U NICODE, est un sur-ensemble de tous les jeux de caractres existants, en particulier de la norme
2 A SCII est lacronyme de American Standard Code for Information Interchange. Ce jeu de caractres est une norme ISO (International Organization for Standardization). 3 E BCDIC

est lacronyme de Extended Binary Coded Decimal.

30

Chapitre 3 Types lmentaires

ISO/CEI 106464 . U NICODE propose trois reprsentations des caractres : UTF32, UTF16 et UTF8, respectivement codes sur 32, 16 et 8 bits. Aujourdhui, la version 5.2 dU NICODE comprend 107 296 caractres qui permettent de reprsenter la majorit des langues et langages utiliss dans le monde. Les 256 premiers caractres du jeu U NICODE sont ceux de la norme ISO 8859, les suivants reprsentent entre autres des symboles de langages varis (dont le braille), des symboles mathmatiques ou encore des symboles graphiques (e.g. dingbats). La description complte du jeu U NICODE est accessible ladresse http://www.unicode.org. Dans tous les langages de programmation, il existe une relation dordre sur les caractres, qui fait apparatre une bijection entre le type caractre et lintervalle dentiers [0, ordmaxcar], o ordmaxcar est lordinal (i.e. le numro dordre) du dernier caractre du jeu de caractres. On peut donc appliquer les oprateurs relationnels sur les objets de type caractre. Le type caractre de J AVA Le type char utilise le jeu de caractres U NICODE. Les caractres sont cods sur 16 bits. Les constantes de type caractre sont dnotes entre deux apostrophes. Ainsi, les caractres a et reprsentent les lettres alphabtiques a et . Il est fondamental de comprendre la diffrence entre les notations 2 et 2. La premire reprsente un caractre et la seconde un entier. Certains caractres non imprimables possdent une reprsentation particulire. Une partie de ces caractres est donne dans la table 3.6.
Caractre passage la ligne tabulation retour en arrire saut de page backslash apostrophe Notation \n \t \r \f \\ \

TAB . 3.6 Caractres spciaux.

Il existe une relation dordre sur le type char. On peut donc appliquer les oprateurs de relation <, <=, =, !=, > et >= sur des oprandes de type caractre. Les caractres peuvent tre dnots par la valeur hexadcimale de leur ordinal, prcde par la lettre u et le symbole \. Par exemple, \u0041 est le caractre dordinal 65, cest--dire la lettre A. Cette notation particulire trouve tout son intrt lorsquil sagit de dnoter des caractres graphiques. Par exemple, les caractres \u12cc et \u1356 reprsentent les lettres thiopiennes et , les caractres \u2200 et \u2208 reprsentent les symboles mathmatiques et , et \u2708 est le symbole v. Notez que puisquen JAVA les caractres U NICODE sont cods sur 16 bits, lintervalle valide va donc de \u0000 \uFFFF. La dclaration suivante introduit trois nouvelles variables de type caractre :
char lettre, marque, symbole;
4 Cette

norme ISO dnit un jeu universel de caracres. U NICODE et ISO/CEI 10646 sont troitement lis.

3.5

Constructeurs de types simples

31

Les caractres U NICODE peuvent tre normalement utiliss dans la rdaction des programmes JAVA pour dnoter des noms de variables ou de fonctions. An daccrotre la lisibilit des programmes, il est fortement conseill dutiliser les caractres accentus, sils sont ncessaires. Il est aussi possible dutiliser toutes sortes de symboles, et la dclaration de constante suivante est tout fait valide :
final double = 3.1415926;

Si la saisie directe du symbole nest pas possible, il sera toujours possible demployer la notation hexadcimale.
final double \u03C0 = 3.1415926;

3.5

CONSTRUCTEURS DE TYPES SIMPLES

Les types numrs et intervalles permettent de construire des ensembles de valeurs particulires. Lintrt de ces types est de pouvoir spcier prcisment le domaine de dnition des variables utilises dans le programme. Certains langages de programmation proposent de tels constructeurs et permettent de nommer les types lmentaires construits. Dans cette section, nous prsentons succinctement une notation algorithmique pour dnir des types numrs et intervalles qui nous serviront par la suite. Nous prsentons galement les types numrs de JAVA, introduits dans sa version 5.0. Les types intervalles nexistent pas en JAVA. Les types numrs Une faon simple de construire un type est dnumrer les lments qui le composent. On indique le nom du type suivi, entre accolades, des valeurs de lensemble construire. Ces valeurs sont des noms de constantes ou des constantes prises dans un mme type lmentaire. Lexemple suivant montre la dclaration de trois types numrs.
couleurs = ( vert, bleu, gris, rouge, jaune ) nbpremiers = ( 1, 3, 5, 7, 11, 13 ) voyelles = ( a , e , i , o , u , y )

Il existe une relation dordre sur les types numrs et, dune faon gnrale, tous les oprateurs relationnels sont applicables sur les types numrs. Les types numrs de J AVA Les numrations de JAVA ne permettent dnumrer que des constantes reprsentes par des noms, comme dans la dclaration prcdente du type couleurs. Ce type, introduit par le mot-cl enum, est dni en JAVA par :
enum Couleurs { vert, bleu, gris, rouge, jaune }

Le fragment de code suivant donne la dclaration dune variable de type Couleurs et son affectation la couleur rouge :
Couleurs c; c = Couleurs.rouge;

32

Chapitre 3 Types lmentaires

Il est important de noter que le type enum de JAVA nest pas un type lmentaire (comme cest le cas dans de nombreux langages de programmation) et que le langage ne dnit pas doprateurs sur les numrations. Ce sont des objets5 , au sens de la programmation objet, issus de la classe java.lang.Enum. Les types intervalles Un type intervalle dnit un intervalle de valeurs sur un type de base. Les types de base possibles sont les types lmentaires et la dclaration doit indiquer les bornes infrieure et suprieure de lintervalle. La forme gnrale dune dclaration dun intervalle est la suivante :
[binf,bsup]

Les bornes infrieure et suprieure sont des constantes de mme type. Les oprations possibles sur les intervalles sont celles admises sur le type de base. Les dclarations suivantes dnissent un type entier naturel et un type lettre alphabtique. Lexemple suivant montre la dclaration de deux types intervalles.
naturel = [0,entmax] lettres = [ a , z ]

3.6

EXERCICES

Exercice 3.1. Parmi les notations de constantes JAVA suivantes, indiquez celles qui sont valides, ainsi que le type des nombres :
0.31 010 33.75 1234 0X1a2 +273.3 .389 1.5+2 3E5 0037 0.005e+3 15 3,250 08 1e2768 0x10 0x5e-4 .E1 10e-4 0x1A2

Exercice 3.2. Indiquez le type de chacune des constantes JAVA donnes ci-dessous :
100 0x10
"a"

true .23
\ u0041

a "nom" \n

2
2 "2"

Exercice 3.3. Est-ce que la disjonction et la conjonction sont des lois commutatives et associatives ? En dautres termes, les galits suivantes sont-elles vries ? (p ou q) ou r = p ou (q ou r) (p et q) et r = p et (q et r) Exercice 3.4. Est-ce que la disjonction est distributive par rapport la conjonction ? Rciproquement, la conjonction est-elle distributive par rapport la disjonction ? Vriez les galits suivantes : (p ou q) et r = (p et q) ou (q et r) (p et q) ou r = (p ou q) et (q ou r)
5 c.f.

chapitre 7.

3.6

Exercices

33

Exercice 3.5. On dit quil y a dpassement de capacit, lorsque les oprations arithmtiques produisent des rsultats en dehors de leur ensemble de dnition. Certains langages de programmation signalent les dpassements de capacit dautres pas. Vriez exprimentalement lattitude de JAVA en cas de dpassement de capacit pour des oprandes de type entier et rel.

Chapitre 4

Expressions

Comme le langage mathmatique, les langages de programmation permettent de composer entre eux des oprandes et des oprateurs pour former des expressions. Les oprandes sont des valeurs ou des noms qui donnent accs une valeur. Ce sont bien videmment des identicateurs de constantes ou de variables, mais aussi des appels de fonctions. Les oprateurs correspondent des oprations qui portent sur un ou plusieurs oprandes. Les oprateurs unaires ou monadiques possdent un unique oprande ; les oprateurs binaires ou dyadiques ont deux oprandes ; ceux qui en possdent trois sont appels ternaires ou triadiques. Un oprateur n oprandes est dit n-aire. Les oprateurs des langages de programmation ont trs rarement plus de trois oprandes, et pour un oprateur donn le nombre doprandes ne varie jamais. Dans la plupart des langages, la notation utilise suit la notation algbrique classique. Cette notation est dite inxe, cest--dire que les oprandes se situent de part et dautre de loprateur, comme dans x + y ou encore x y + z. Elle ncessite des parenthses pour exprimer par exemple des rgles de priorit, x + y z est diffrent de (x + y) z. Certains langages, comme L ISP, utilisent la notation polonaise1 , galement appele prxe, qui place loprateur systmatiquement avant ses oprandes. On crit par exemple + x y ou + x y z. Les parenthses sont inutiles, + x y z est diffrent de + x y z. Remarquez que lappel dune fonction ou dune procdure est considrer comme une notation prxe, o loprateur est le nom de la fonction ou de la procdure, et ses oprandes sont les paramtres effectifs. La notation polonaise inverse, appele aussi notation postxe, place loprateur la suite de ses oprandes. Les possesseurs de calculettes HP connaissent bien cette notation qui im1 Ainsi

appele car son invention est due au mathmaticien polonais JAN UKASIEWICW.

36

Chapitre 4 Expressions

pose une criture des expressions de la forme x y + ou x y z +. Avec cette notation, les parenthses sont galement inutiles puisque (x + y)/z scrit x y + z /. Dans la suite de ce chapitre, nous nutiliserons que la notation inxe dans la mesure o elle est la plus employe par les langages de programmation.

4.1

VALUATION

Le but dune expression est de calculer, lors de son valuation, un rsultat. En gnral, lvaluation dune expression produit un rsultat unique. Mais pour certains langages, comme par exemple I CON [GG96], lvaluation dune expression peut donner aucun, un ou plusieurs rsultats. Le rsultat dune expression est dtermin par lordre dvaluation des formules simples qui la composent. Cet ordre dvaluation, pour une mme forme syntaxique, nest pas forcment le mme dans tous les langages. En supposant que les oprandes sont tous de mme type, nous considrerons trois cas : la composition dun mme oprateur, la composition doprateurs diffrents et lutilisation de parenthseurs.

4.1.1

Composition du mme oprateur plusieurs fois

Lordre dvaluation nest important que dans la mesure o loprateur nest pas associatif. Dans la plupart des langages de programmation, la grammaire prcise que lordre dvaluation est de gauche droite. Cest le cas pour la majorit des oprateurs ; on dit quils sont associatifs gauche. Ainsi, lexpression x + y + z calcule la somme de x + y et de z. Plus rarement, les langages proposent des oprateurs associatifs droite. Ceux qui autorisent la composition daffectations dnissent un ordre dvaluation de droite gauche de cet oprateur ; x y z commence par affecter y la valeur de z, puis affecte x la valeur de y.

4.1.2

Composition de plusieurs oprateurs diffrents

Lordre de gauche droite nest pas toujours le plus naturel lorsque les oprateurs concerns sont diffrents. An de respecter les habitudes de notation algbrique, la plupart des langages de programmation dnissent entre les oprateurs des rgles de priorit susceptibles de remettre en cause lordre dvaluation de gauche droite. Par exemple, x + y z correspond laddition de x et du produit y z, et non au produit de x + y avec z. Rgles de priorit en J AVA Les rgles de priorit varient considrablement dun langage de programmation lautre, et il est bien difcile de dgager des rgles communes. La table 4.1 donne les rgles de priorit du langage JAVA, en allant du moins prioritaire au plus prioritaire, des oprateurs vus au chapitre prcdent. niveau de priorit gal, lvaluation se fait de gauche droite, sauf pour laffectation.

4.2

Type dune expression

37

oprateur = || && | & == != < > >= <= + * / % ! ()

fonction affectation disjonction conditionnelle conjonction conditionnelle disjonction conjonction galit, ingalit oprateurs relationnels oprateurs additifs oprateurs multiplicatifs ngation sous-expression

TAB . 4.1 Rgles de priorit des oprateurs J AVA.

4.1.3

Parenthsage des parties dune expression

Il arrive que les deux modes de composition prcdents ne permettent pas dexprimer lordre dans lequel doit seffectuer les oprations. Ainsi, si lon veut additionner x et y, puis multiplier le rsultat par z, il est ncessaire de recourir la notion de parenthsage. Lexpression scrit alors, avec des parenthses, (x + y) z. Les parenthses permettent de ramener toute sous-expression dune mme expression au rang dun et dun seul oprande, que lon peut alors composer comme dhabitude avec le reste de lexpression. Ainsi, dans lexpression (x + y) z, loprateur multiplicatif possde deux oprandes x + y et z.

4.2

TYPE DUNE EXPRESSION

On vient de voir quune expression calcule un rsultat. Ce rsultat est typ et dnit, par voie de consquence, le type de lexpression. Ainsi, si p et q sont deux boolens, lexpression p ou q est une expression boolenne puisquelle produit un rsultat boolen. Notez bien quune expression peut trs bien tre forme partir doprateurs manipulant des objets de types diffrents, et dans ce cas le parenthsage peut servir dlimiter sans ambigut les oprandes de mme type dont la composition forme un rsultat dun autre type, lui-mme oprande dun autre oprateur dans la mme expression. Par exemple, lexpression boolenne suivante :

(i

maxlments) et (courant = 0)

est forme par les deux oprandes boolens de loprateur de conjonction eux-mmes composs partir de deux oprandes numriques relis par des oprateurs de relation rsultat boolen.

38

Chapitre 4 Expressions

4.3

CONVERSIONS DE TYPE

Les objets, mais pas tous, peuvent tre convertis dun type vers un autre. Gnralement, on distingue deux types de conversion. Les conversions implicites pour lesquelles loprateur dcide de la conversion faire en fonction de la nature de loprande ; les conversions explicites, pour lesquelles le programmeur est responsable de la mise en uvre de la conversion laide dune notation adquate. Dans les langages fortement typs, les conversions implicites sont, pour des raisons de scurit de programmation, peu nombreuses. Dans un langage comme PASCAL, il nexiste quune seule conversion implicite des entiers vers les rels. Toutefois, il existe des exceptions, comme par exemple le langage C, qui dnit de nombreuses conversions implicites. En revanche, les langages non typs, de par leur nature, ont en gnral un nombre de conversions implicites importants et il nest pas toujours simple pour le programmeur de dterminer rapidement le type du rsultat de lvaluation dune expression. Les conversions de type en J AVA Les conversions de type implicites sont relativement nombreuses, en partie dues lexistence de plusieurs types entiers et rels. Ces conversions, appeles galement promotions, transforment implicitement un objet de type T en un objet dun type T , lorsque le contexte lexige. Par exemple, lvaluation de lexpression 1 + 4.76 provoquera la conversion implicite de lentier 1 en un rel double prcision 1.0, suivie dune addition relle. Les promotions possibles sont donnes par la table 4.2.
type float long int short char promotion double float ou double long, float ou double int, long, float ou double int, long, float ou double

TAB . 4.2 Promotions de type du langage J AVA.

Les conversions explicites, appeles cast en anglais, sont faites laide dun oprateur de conversion. Sa dnotation consiste placer entre parenthses, devant la valeur convertir, le type dans lequel elle doit tre convertie. Dans lexemple suivant, le rel 1.4 est converti explicitement en un entier. Le rsultat de la conversion (int) 1.4 est lentier 1.

4.4

UN EXEMPLE

On dsire crire un programme qui lit sur lentre standard une valeur reprsentant une somme dargent et qui calcule et afche le nombre de billets de 500, 200, 100, 50 et 10 euros quelle reprsente.

4.4

Un exemple

39

Lalgorithme Lalgorithme commence par lire sur lentre standard lentier qui reprsente la somme dargent et affecte la valeur une variable capital. Pour obtenir la dcomposition en nombre de billets et de pices de la somme dargent, on procde par des divisions entires successives en conservant chaque fois le reste. La preuve de la validit de lalgorithme sappuie sur la dnition de la division euclidienne : a, b N, b > 0 a = q b + r et 0

r<b

Le quotient est obtenu par loprateur de division entire, et le reste par celui de modulo. Cet algorithme, avec les assertions qui dmontrent la validit du programme, sexprime comme suit :
Algorithme Somme dargent variables capital, reste, b500, b200, b100, b50, b10 type entier {il existe sur lentre standard un entier qui reprsente une somme dargent en euros} lire(capital) {capital = somme dargent > 0 et multiple de 10} capital b500 500 reste capital modulo 500 {capital = b500500 + reste} reste b200 200 reste reste modulo 200 {capital = b500500 + b200200 + reste} reste b100 100 reste reste modulo 100 {capital = b500500 + b200200 + b100100 + reste} reste b50 50 reste reste modulo 50 {capital = b500500 + b200200 + b100100 + b5050 + reste} reste b10 10 {capital = b500500 + b200200 + b100100 + b5050 + b1010} crire(b500, b200, b100, b50, b10) {le nombre de billets de 500, 200, 100, 50 et 10 euros reprsentent la valeur de capital}

Le programme en J AVA partir de lalgorithme prcdent, et des connaissances JAVA dj acquises, la rdaction du programme ne pose pas de relles difcults.

40

Chapitre 4 Expressions

/** La classe SommedArgent lit sur lentre standard une valeur * reprsentant une somme dargent multiple de 10, puis calcule * et affiche le nombre de billets de 500, 200, 100, 50 et 10 euros * quelle reprsente */ import java.io.*; class SommedArgent { public static void main (String[] args) throws IOException { int capital = StdInput.readlnInt(), reste, b500, b200, b100, b50, b10; assert capital>0 && capital%10==0; b500 = capital / 500; reste = capital % 500; assert capital == b500*500+reste; b200 = reste / 200; reste %= 200; assert capital == b500*500+b200*200+reste; b100 = reste / 100; reste %= 100; assert capital == b500*500+b200*200+b100*100+reste; b50 = reste / 50; reste %= 50; assert capital == b500*500+b200*200+b100*100+b50*50+reste; b10 = reste / 10; assert capital == b500*500+b200*200+b100*100+b50*50+b10*10; System.out.println(b500+" "+b200+" "+b100+" "+b50+" "+b10); } } // fin classe SommedArgent

Toutefois, dans ce programme, vous pouvez remarquez plusieurs choses nouvelles : linitialisation de la variable capital au moment de sa dclaration, lutilisation dun nouvel oprateur daffectation %=, et enn lemploi de lnonc assert. Regrouper la dclaration et linitialisation dune variable a pour intrt de clairement localiser ces deux actions. Nous considrerons cela comme une bonne technique de programmation. Laffectation compose reste%=200 est quivalente laffectation reste=reste%200. Dune faon gnrale, une affectation de la forme a op= b est quivalente a=a op b, o op peut tre choisi parmi +, -, *, /, %, &,| et encore quelques autres oprateurs dont nous ne parlerons pas pour linstant. Lintrt principal de ces oprateurs est de nvaluer quune seule fois loprande gauche. Les affectations de JAVA ont la particularit2 dtre des expressions, cest--dire de fournir un rsultat, en loccurrence la valeur de loprande gauche aprs affectation. Nos deux premiers programmes JAVA nutilisent pas cette caractristique, mais nous verrons un peu plus tard quelle inuence la faon de programmer les algorithmes.
2 On la trouve galement dans les langages C et C++ plus gnralement dans les langages dexpression. Dans ces derniers, toute instruction fournit un rsultat.

4.5

Exercices

41

Les afrmations sous forme de commentaires dans lalgorithme qui montrent sa validit ont t remplaces par des noncs assert. Lexpression boolenne qui suit le mot-cl assert est value lexcution du programme et provoque une erreur3 si lexpression nest pas vraie au moment de son valution.

4.5

EXERCICES

Exercice 4.1. Parmi les dclarations de variables JAVA suivantes, indiquez celles qui sont valides :
int i = 0; short j = 60000; char c = a; boolean b = true; float f = 0.1; float f = 0x10; short j; int i = 0x10; char c = 0x41; boolean b = 0; double d = 0.1; double d = .1; long l1, l2 = 0, l3; char c = a ; char c = \ u0041 ; real r = 0.1; double d = 0; int i = a ;

Exercice 4.2. Trouvez lerreur prsente dans le fragment de programme suivant :


final double ; = 3.1415926535897931; final float e = 2.7182818284590451f; = e;

Exercice 4.3. En fonction des dclarations de variables :


int i, j, k; double x, y, z; char c; boolean b;

indiquez le type de chacune des expressions suivantes :


x x x x i i + 2.0 / 2 < y && b = c 2 x x i i x + 2 / 2.0 % j + y == j && b = (int) y i i i i i c = + / / > = j 2 2 j + y j && k > j (char) (int) c + 1) i == j x + i i / 2.0 i > j > k x + y * i i++

Exercice 4.4. crivez en JAVA les trois expressions suivantes : a2 c + bc + a c d+


3 Plus

b+ e f

b2 4ac 2a

1 1 + a b c+d

prcisment une exception, c.f. le chapitre 13.

Chapitre 5

noncs structurs

Les actions que nous avons tudies jusqu prsent sont des actions lmentaires. Une action structure est forme partir dautres actions, qui peuvent tre elles-mmes lmentaires ou structures. Dans ce chapitre, nous prsenterons deux premires actions structures, lnonc compos et lnonc conditionnel, et pour chacune dentre elles la rgle de dduction qui permet den vrier la validit.

5.1

NONC COMPOS

Comme pour les expressions, il est possible de parenthser une suite dactions. Lnonc compos groupe des actions qui sont ensuite considres comme une seule action. Dans notre notation algorithmique, les noncs composer seront placs entre les deux parenthseurs dbut et fin. Par exemple, la composition de trois noncs E1 , E2 , E3 scrit de la faon suivante :
dbut E1 E2 E3 fin

Les trois noncs sont excuts de faon squentielle, cest--dire successivement les uns aprs les autres. Lnonc E2 ne pourra tre trait quune fois lexcution de lnonc E1 acheve. De mme, lexcution de E3 ne commence quaprs la n de celle de E2 . La notion dexcution squentielle est mettre en opposition avec celle dexcution parallle, qui permettrait le traitement simultan des trois noncs. La vrication de la validit des algorithmes parallles, surtout si les actions sexcutent de faon synchrone, est beaucoup plus complexe et difcile mettre en uvre que celle des algorithmes squentiels. Les algorithmes prsents dans cet ouvrage sont exclusivement squentiels.

44

Chapitre 5 noncs structurs

Rgle de dduction La rgle de dduction dun nonc compos sexprime de la faon suivante :
si {P } {P1 } {Q1 } {P2 } {Q2 } ... {Pn } {Qn } {Q} alors {P } dbut {P1 } E1 {Q1 } {P2 } E2 {Q2 } ... {Pn } En {Qn } fin {Q}
E1 E2 En

La notation {P } {Q} exprime que le consquent Q se dduit de lantcdent P par lapplication de lnonc E. Sil ny a pas dnonc, la notation {P } {Q} indique que Q se dduit directement de P . La rgle de dduction prcdente spcie que si la pr-condition
{P } {P1 } {Q1 } {P2 } {Q2 } ... {Pn } {Qn } {Q}
E1 E2 En

est vrie alors le consquent Q se dduit de lantcdent P par application de lnonc compos. Lnonc compos en J AVA Les parenthseurs sont reprsents par les accolades ouvrantes et fermantes. La plupart des langages de programmation utilise un sparateur entre les noncs, qui doit tre considr comme un oprateur de squentialit. En JAVA, il ny a pas proprement parl de sparateur dnonc. Toutefois, un point-virgule est ncessaire pour terminer un nonc simple.

5.2

NONCS CONDITIONNELS

Les actions qui forment les programmes que nous avons crits jusqu prsent sont excutes systmatiquement une fois. Les langages de programmation proposent des noncs conditionnels qui permettent dexcuter ou non une action selon une dcision prise en fonction dun choix. Le critre de choix est en gnral la valeur dun objet dun type lmentaire discret.

5.2.1

nonc choix

Dans la vie courante, nous avons quotidiennement des dcisions prendre, souvent simples, et parfois difciles. Imaginons un automobiliste abordant une intersection routire contrle par un feu de signalisation. Respectueux du code de la route, il sait que si le feu est rouge, il doit sarrter ; si le feu est vert, il peut passer ; si le feu est orange, il doit sarrter si cela est possible, sinon il passe. Dun point de vue informatique, il est possible de modliser le comportement de lautomobiliste laide dun nonc choix. Le critre de choix est la couleur du feu dont le domaine de dnition est lensemble form par les trois couleurs rouge, vert et orange. chacune de ces valeurs est associe une certaine action. Nous pouvons crire ce choix de la faon suivante :
choix couleur du feu parmi rouge : sarrter vert : passer orange : sarrter si possible, sinon passer finchoix

5.2

noncs conditionnels

45

Dune faon plus formelle, lnonc choix prcise lexpression dont lvaluation fournira la valeur de lobjet discriminatoire, puis il donne la liste des actions possibles, chacune tant prcde de la valeur correspondante. Lnonc choix scrit :
choix expr val1 : val2 : . . . valn : finchoix parmi E1 E2

En

Lexpression est value et seul lnonc qui correspond au rsultat obtenu est excut. Que se passe-t-il si lvaluation de lexpression renvoie une valeur non dnie dans lnonc ? En gnral, plutt que de signaler une erreur, les langages choisissent de nexcuter aucune action. Rgle de dduction La rgle de dduction de lnonc choix sexprime de la faon suivante :
si k [1, n], {P et expr = valk } {Qk } alors {P } nonc-choix {Q}
Ek

En pratique, {Q} peut tre lunion de consquents {Qk } des noncs Ek . Notez que le consquent doit tre vri, mme si aucun nonc Ek na t excut. Lnonc choix en J AVA La notion de choix est mise en uvre grce lnonc switch. Lexpression, dnote entre parenthses, doit rendre une valeur dun type discret : un type entier, caractre ou numr. Chaque valeur de la liste des valeurs possibles est introduite par le mot-cl case, et il existe une valeur spciale, appele default. cette dernire, il est possible dassocier un ou plusieurs noncs, qui sont excuts lorsque lexpression renvoie une valeur qui ne fait pas partie de la liste des valeurs numres. De plus, le programmeur doit explicitement indiquer, laide de linstruction break, la n de lnonc slectionn et lachvement de lnonc switch. On peut regretter que le mcanisme de terminaison de lnonc switch ne soit pas automatique comme le proposent dautres langages. Lexemple prcdent scrit en JAVA comme suit :
switch (couleurDuFeu) { case rouge : sarrter; break; case vert : passer; break; case orange : sarrter si possible, sinon passer; break; }

5.2.2

nonc si

Lorsque lnonc choix est gouvern par la valeur dun prdicat binaire, cest--dire une expression boolenne, comme dans la phrase suivante : si mon salaire augmente alors je reste sinon je change dentreprise

46

Chapitre 5 noncs structurs

on est dans un cas particulier de lnonc choix, appel lnonc si. Au lieu dcrire :
choix mon salaire augmente parmi vrai : je reste faux : je change dentreprise finchoix

on prfrera la formule suivante, plus proche du langage courant :


si mon salaire augmente alors je reste sinon je change dentreprise finsi

Dune faon gnrale, cet nonc conditionnel sexprime de la faon suivante :


si B alors E1 sinon E2 finsi

Lnonc E1 est excut si le prdicat boolen B est vrai, sinon ce sera lnonc E2 qui le sera. Rgle de dduction La rgle de dduction de lnonc si est donne ci-dessous :
si {P et B} {Q1 } et {P et non B} {Q2 } alors {P } nonc-si {Q}
E1 E2

Lexcution de lnonc E1 ou celle de E2 doit conduire au consquent {Q}. Notez que ce dernier peut tre lunion de deux consquents particuliers {Q1 } et {Q2 } des noncs E1 et E2 . Forme rduite Il est frquent que laction effectuer dans le cas o le prdicat boolen est faux soit vide. La partie sinon est alors omise et on obtient une forme rduite de lnonc si. Dune faon gnrale, cette forme scrit :
si B alors E finsi

Par exemple, pour obtenir la valeur absolue dun entier, nous crirons lnonc suivant :
{x est un entier quelconque} si x < 0 alors x -x finsi {x 0}

Rgle de dduction La rgle de dduction de la forme rduite de lnonc si sexprime de la faon suivante :
si {P et B} {Q} et {P et non B} {Q} alors {P } nonc-si-rduit {Q}
E

Notez que si lnonc E nest pas excut, le consquent {Q} doit tre vri.

5.3

Rsolution dune quation du second degr

47

Lnonc si en J AVA Lnonc si et sa forme rduite scrivent en JAVA de la faon suivante :


if (B) E1 else E2 if (B) E

Notez que cette syntaxe ne sembarrasse pas des mots-cls alors et finsi mais au dtriment dune certaine lisibilit, et a pour consquence lobligation de mettre systmatiquement le prdicat boolen entre parenthses.

5.3

RSOLUTION DUNE QUATION DU SECOND DEGR

Avec lnonc conditionnel, il nous est dsormais possible dcrire des programmes plus consquents. La programmation de la rsolution dune quation du second degr a ceci dintressant quelle est dune taille sufsamment importante (mais pas trop) pour mettre en vidence, dune part, la construction progressive dun algorithme, et dautre part, linuence de linexactitude de larithmtique relle sur lalgorithme. Posons le problme. On dsire crire un programme qui calcule les racines dune quation non dgnre du second degr. Formalisons un peu cet nonc : soient a, b et c, trois coefcients rels dune quation du second degr avec a=0, calculer les racines r1+ii1 et r2+ii2 solutions de lquation. La suite dactions qui assure ce travail peut tre rduite une action lmentaire, qui a pour donnes les trois coefcients a, b et c et pour rsultats les parties relles (r1 et r2) et imaginaires (i1 et i2) des deux racines. Ce que nous crivons :
{Antcdent : a=0, b et c rels, coefficients de lquation du second degr, ax2 +bx+c} calculer les racines de lquation {Consquent : (x-(r1+ii1)) (x-(r2+ii2)) = 0}

Sil existe une procdure prdnie qui calcule les racines et qui respecte le consquent nal, alors il suft de lappeler et notre travail est termin. Dans le cas contraire, nous devons procder au calcul. Un premier niveau de rexion peut tre le suivant. Regardons la valeur du discriminant . Si 0, les racines sont relles, sinon elles sont complexes. Cela se traduit par lalgorithme :
Algorithme quation du second degr {Antcdent : a=0, b et c rels, coefficients de lquation du second degr, ax2 +bx+c} calculer le discriminant si 0 alors calculer les racines relles sinon calculer les racines complexes finsi {Consquent : (x-(r1+ii1)) (x-(r2+ii2)) = 0}

48

Chapitre 5 noncs structurs

Dans une seconde tape, il est ncessaire de dtailler les trois parties nonces de faon informelle et qui se dgagent de lalgorithme prcdent. Notez que ces trois parties sont indpendantes, et quelles peuvent tre traites dans un ordre quelconque. 1. Le calcul du discriminant scrit directement. Cest une simple expression mathmatique : carr(b)-4ac. 2. Pour le calcul des racines relles, il est absolument ncessaire de tenir compte du fait que nous travaillons sur des objets de type rel, et que larithmtique relle sur les ordinateurs est inexacte. Le calcul direct des racines selon les formules mathmatiques habituelles est dangereux, en particulier, si lopration daddition ou de soustraction conduit soustraire des valeurs presque gales. Pour viter cette situation, on calcule dabord la racine la plus grande en valeur absolue. Si b>0, alors on calcule (-b- )/2a, sinon cest (-b+ )/2a. Une fois ce calcul effectu, la seconde racine se dduit de la premire par le produit r1 r2 = c/a, sauf si la premire est nulle. Auquel cas, la seconde lest aussi. Enn, dans le cas rel, les deux parties imaginaires sont nulles. Les oprations de comparaison sont elles aussi dangereuses, en particulier lopration dgalit. La comparaison dun nombre avec zro devra se faire un epsilon prs. En tenant compte de tout ce qui vient dtre crit, lalgorithme du calcul des racines relles scrit de la faon suivante :
{calcul des racines relles} si b>0 alors r1 -(b+ )/(2a) sinon r1 ( -b)/(2a) finsi {r1 est la racine la plus grande en valeur absolue} si |r1| < alors r2 0 sinon r2 c/(ar1) finsi i1 0 i2 0 {(x-r1)(x-r2)=0}

3. Le calcul des racines complexes ne pose pas de problme particulier. Les racines complexes sont donnes par les expressions suivantes :
r1 r2 -b/(2a) p ()/(2a) i1 i2 -i1

En rassemblant les diffrentes parties, lalgorithme complet de rsolution dune quation du second degr non dgnre est le suivant :
Algorithme quation du second degr {Antcdent : a=0, b et c rels coefficients de lquation du second degr, ax2 +bx+c} {Consquent : (x-(r1+ii1)) (x-(r2+ii2)) = 0} constante = ? {dpend de la prcision des rels sur la machine}

5.3

Rsolution dune quation du second degr

49

variables , a, b, c, r1, r2, i1, i2 type rel {a=0, b et c coefficients rels de lquation du second degr, ax2 +bx+c} carr(b)-4ac si 0 alors {calcul des racines relles} si b>0 alors r1 -(b+ )/(2a) sinon r1 ( -b)/(2a) finsi {r1 est la racine la plus grande en valeur absolue} si |r1|< alors r2 0 sinon r2 c/(ar1) finsi i1 0 i2 0 {(x-r1)(x-r2)=0} sinon {calcul des racines complexes} r1 r2 -b/(2a) p i1 ()/(2a) i2 -i1 finsi {(x-(r1+ii1)) (x-(r2+ii2)) = 0}

Lcriture en JAVA du programme est maintenant aise, il sagit dune simple transcription de lalgorithme prcdent. Insistons encore sur le fait que la tche la plus difcile, lors de la construction dun programme, est la conception de lalgorithme et non pas son criture dans un langage particulier.
/** La classe q2Degr rsout une quation du second degr * non dgnre, partir de ses trois coefficients lus sur * lentre standard. Elle affiche sur la sortie standard les * racines solutions de lquation. */ import java.io.*; class q2Degr { public static void main (String[] args) throws IOException { final double = 01e-100; // prcision du calcul double a = StdInput.readDouble(), b = StdInput.readDouble(), c = StdInput.readlnDouble(), r1, r2, i1, i2, ; // a<>0, b et c coefficients rels de // lquation du second degr, ax2 +bx+c = (b*b)-4*a*c; if (>=0) { //calcul des racines relles if (b>0) r1 = -(b+Math.sqrt())/(2*a); else r1 = (Math.sqrt()-b)/(2*a); // r1 est la racine la plus grande en valeur absolue

50

Chapitre 5 noncs structurs

r2 = Math.abs(r1) < ? 0 : c/(a*r1); i1 = i2 = 0; // (x-r1)(x-r2)=0 } else { //calcul des racines complexes r1 = r2 = -b/(2*a); i1 = Math.sqrt(-)/(2*a); i2 = -i1; } // (x-(r1+ii1)) (x-(r2+ii2)) = 0 // crire les racines solutions sur la sortie standard System.out.println(" r1= ( " + r1 + " , " + i1 + " ) "); System.out.println(" r2= ( " + r2 + " , " + i2 + " ) "); } } //fin classe q2Degr

Les noms de constantes et de variables

et correspondent aux caractres U NICODE

\u03b5 et \u0394. Remarquez que ce programme emploie une nouvelle construction du

langage JAVA, lexpression conditionnelle (le seul oprateur ternaire du langage). Celle-ci a la forme suivante :
exp1 ? exp2 : exp3

Lexpression boolenne exp1 est value. Si le rsultat est vrai, le rsultat de lexpression conditionnelle est le rsultat de lvaluation de lexpression exp2 ; sinon le rsultat est celui de lvaluation de exp3. Notez que le calcul de r2 aurait tout aussi bien pu scrire :
if (Math.abs(r1) < )) r2 = 0; else r2 = c/(a*r1);

5.4

EXERCICES

Exercice 5.1. crivez lalgorithme de laddition de deux entiers x et y, lus sur lentre standard, qui vrie quelle ne provoque pas un dpassement de capacit. Vous prouverez la validit de votre algorithme en appliquant les rgles de dduction de lnonc conditionnel si. Exercice 5.2. crivez un programme qui calcule les deux racines relles dune quation du second degr, partir des formules mathmatiques classiques, sans tenir compte de linexactitude de larithmtique relle. Cherchez des valeurs pour les coefcients a, b et c qui montrent que la mthode prsente dans ce chapitre fournit des rsultats plus satisfaisants.

Chapitre 6

Procdures et fonctions

Dans une approche de la programmation organise autour des actions, les programmes sont dabord structurs laide de procdures et de fonctions. linstar de C ou PASCAL, les langages qui suivent cette approche sont dits procduraux. Nous avons vu prcdemment comment utiliser une procdure et une fonction prdnie ; dans ce chapitre, nous tudierons comment les construire et le mcanisme de transmission des paramtres lorsquelles sont appeles.

6.1

INTRT

Il arrive frquemment quune mme suite dnoncs doive tre excute en plusieurs points du programme. Une procdure ou une fonction permet dassocier un nom une suite dnoncs, et dutiliser ce nom comme abrviation chaque fois que la suite apparat dans le programme. Bien souvent, dans la terminologie des langages procduraux, le terme sous-programme est utilis pour dsigner indiffremment une procdure ou une fonction. Lassociation dun nom la suite dnoncs se fait par une dclaration de sous-programme. Lutilisation du nom la place de la suite dnoncs sappelle un appel de procdure ou de fonction. Si la plupart des langages de programmation possde la notion de procdures et de fonctions, cest bien parce quelle est un outil fondamental de lart de la programmation dont la matrise inue de faon dcisive sur le style et la qualit du travail du programmeur [Wir75].

52

Chapitre 6 Procdures et fonctions

Les sous-programmes ont un rle trs important dans la structuration et la localisation, le paramtrage et la lisibilit des programmes : Bien plus quune faon dabrger le texte du programme, cest un vritable outil de structuration des programmes en composants ferms et cohrents. Nous avons vu dans lintroduction, que la programmation descendante par rafnements successifs divisait le problme en sous-parties cohrentes. Les sous-programmes seront naturellement les outils adapts la description de ces sous-parties. Une suite dnoncs pourra tre dclare comme sous-programme mme si celle-ci nest excute quune seule fois. Il arrive frquemment que, pour des besoins de calculs intermdiaires, une suite dnoncs ait besoin de dnir des variables (ou plus gnralement des objets) qui sont sans signication en dehors de cette suite, comme par exemple la variable dans le calcul des racines dune quation du second degr. Les procdures ou fonctions seront des units textuelles permettant de dnir des objets locaux dont le domaine de validit sera bien dlimit. Certaines suites dnoncs ont de fortes ressemblances, mais ne diffrent que par la valeur de certains identicateurs ou expressions. On aimerait que la suite dnoncs, qui calcule les racines de lquation du second degr donne au chapitre prcdent, puisse faire ce calcul avec des coefcients chaque fois diffrents. Le mcanisme de paramtrage dune fonction ou dune procdure nous permettra de considrer la suite dnoncs comme un schma de calcul abstrait1 dont les paramtres reprsenteront des valeurs particulires chaque excution particulire de la suite dnoncs. Une consquence de leffet de structuration des programmes laide des sousprogrammes est laugmentation de la lisibilit du code. De plus, les procdures et fonctions amlioreront la documentation des programmes.

6.2

DCLARATION DUN SOUS-PROGRAMME

Le rle de la dclaration dun sous-programme est de lier un nom unique une suite dnoncs sur des objets formels ne prenant des valeurs effectives quau moment de lappel de ce sous-programme. Le nom est un identicateur de procdure ou de fonction. Cette dclaration est toujours forme dun en-tte et dun corps. Len-tte Len-tte du sous-programme, appel aussi signature ou encore prototype, spcie : le nom du sous-programme ; les paramtres formels et leur type ; le type du rsultat dans le cas dune fonction. La dclaration dune procdure prend la forme suivante :
procdure NomProc ([ <liste de paramtres formels> ]) {Antcdent: une affirmation} {Consquent: une affirmation} {Rle: une affirmation donnant le rle de la procdure}

1 ibidem.

6.3

Appel dun sous-programme

53

Notez que les crochets indiquent que la liste des paramtres formels est facultative. Tous les en-ttes de procdures doivent contenir des afrmations dcrivant leur antcdent, leur consquent ou leur rle. Len-tte correspondant la dclaration de la procdure qui calcule les racines dune quation du second degr peut tre :
procdure q2degr(donnes a, b, c : rel rsultats r1, i1, r2, i2 : rel) {Antcdent : a=0, b et c rels, coefficients de lquation du second degr, ax2 +bx+c} {Consquent : (x-(r1+ii1)) (x-(r2+ii2)) = 0} {Rle : calcule les racines dune quation du second degr}

La procdure sappelle q2degr, possde sept paramtres formels a, b, c qui sont les donnes de la procdure, et r1, i1, r2 et i2 qui sont les rsultats quelle calcule. Tous ces paramtres sont de type rel. Une fonction est un sous-programme qui reprsente une valeur rsultat qui peut intervenir dans une expression. La dclaration dune fonction prcise en plus le type du rsultat renvoy :
fonction NomFonc ([ <liste de paramtres formels> ]) : type-rsultat {Antcdent : une affirmation} {Consquent : une affirmation} {Rle : une affirmation donnant le rle de la fonction}

Len-tte suivant dclare une fonction qui retourne la racine carre dun entier naturel :
fonction rac2(donne x : naturel) : rel {Antcdent : x 0} x} {Consquent : rac2 = {Rle : calcule la racine carre de lentier naturel x}

Le corps Le corps du sous-programme contient la suite dnoncs. Nous le dlimiterons par les mots-cls finproc ou finfonc, et il se place la suite de len-tte du sous-programme.
{corps de la procdure q2degr} ... suite dnoncs ... finproc {q2degr}

{corps de la fonction rac2} ... suite dnoncs ... finfonc {rac2}

6.3

APPEL DUN SOUS-PROGRAMME

Lappel dun sous-programme est une action lmentaire qui permet lexcution de la suite dnoncs associe son nom. Il consiste simplement indiquer le nom de la procdure ou

54

Chapitre 6 Procdures et fonctions

de la fonction avec ses paramtres effectifs.


{appel de la procdure q2degr} q2degr(u,1,v+1,rl1,ig1,rl2,ig2) {deux appels de la fonction rac2} crire(rac2(5), rac2(tangente))

6.4

TRANSMISSION DES PARAMTRES

Un paramtre formel est un nom sous lequel un paramtre dun sous-programme est connu lintrieur de celui-ci lors de sa dclaration. Un paramtre effectif est lentit fournie au moment de lappel du sous-programme, sous la forme dun nom ou dune expression. Nous distinguerons deux types de paramtres formels : les donnes et les rsultats. Les paramtres donnes fournissent les valeurs partir desquelles les noncs du corps du sousprogramme effectueront leur calcul. Les paramtres rsultats donnent les valeurs calcules par la procdure. Une procdure peut avoir un nombre quelconque de paramtres donnes ou rsultats. En revanche, une fonction, puisquelle renvoie un rsultat unique, naura que des paramtres donnes. Dans bien des langages de programmation, rien ninterdit de dclarer dans len-tte dune fonction des paramtres rsultats, mais nous considrerons que cest une faute de programmation. Le remplacement des paramtres formels par les paramtres effectifs au moment de lappel du sous-programme se fait selon des rgles strictes de transmission des paramtres. Pour de nombreux des langages de programmation, le nombre de paramtres effectifs doit tre identique celui des paramtres formels, et la correspondance entre les paramtres formels et effectifs se fait sur la position. De plus, ils doivent tre de type compatible (e.g. un paramtre effectif de la fonction rac2 ne pourrait pas tre de type boolen). Nous utiliserons cette convention.
procdure P(donnes a, b : entier rsultat c : entier) {Antcdent : ... } {Consquent : ... } {Rle : ... } {dbut du corps de P} ... finproc {P} {appel de P} P(x,y,z)

Les paramtres effectifs x, y et z correspondent, respectivement, aux paramtres formels


a, b et c.

Parmi tous les modes de transmission de paramtres que les langages dnissent (et il en existe de nombreux), nous allons en prsenter deux : la transmission par valeur et la transmission par rfrence.

6.5

Retour dun sous-programme

55

6.4.1

Transmission par valeur

La transmission par valeur est utilise pour les paramtres donnes. Elle a pour effet daffecter au nom du paramtre formel la valeur du rsultat de lvaluation du paramtre effectif. Le paramtre effectif sert fournir une valeur initiale au paramtre formel. Dans lappel P(xe ) dune procdure P dont len-tte est le suivant :
procdure P(donne xf : T)

tout se passe comme si, avant dexcuter la suite dnoncs de la procdure P, laffectation xf xe tait effectue. Notez que le paramtre effectif peut donc tre un nom ou une expression. Dautre part, toute modication du paramtre formel reste locale au sous-programme, cela veut dire quun paramtre effectif transmis par valeur ne peut tre modi.

6.4.2

Transmission par rsultat

De quelle faon un sous-programme renvoie-t-il ses rsultats ? Une fonction par sa valeur de retour et une procdure utilisera un ou plusieurs paramtres dont le mode de transmission est par rsultat. Ce mode de transmission est prcis en faisant prcder le nom du paramtre formel par le mot-cl rsultat. Dans lappel P(xe ) dune procdure P dont len-tte est le suivant :
procdure P(rsultat xf : T)

tout se passe comme si, en n dexcution de la procdure P, le paramtre effectif xe tait modi par laffectation xe xf . Les paramtres effectifs transmis par rsultat sont ncessairement des noms, puisquils apparaissent en partie droite de laffectation, et en aucun cas des expressions. Notez quune fonction ne devra comporter aucun paramtre transmis par rsultat, puisque par dnition elle renvoie un seul rsultat dnot par lappel de fonction. Dautre part, le mode de transmission dun paramtre qui est la fois donne et rsultat est le mode par rsultat. Les rgles de transmission prcdentes sont appliques lentre et la sortie du sous-programme.

6.5

RETOUR DUN SOUS-PROGRAMME

Lendroit o se fait lappel du sous-programme sappelle le contexte dappel. Ce contexte peut tre soit le programme principal, soit un autre sous-programme. Aprs lappel, les noncs du sous-programme appel sont excuts. lissue de leur excution, le sous-programme sachve et le prochain nonc excut est celui qui suit immdiatement lnonc dappel du sous-programme dans le contexte dappel. Nous avons vu que lappel dune fonction dlivrait un rsultat. Comment est spci ce rsultat dans la fonction appele ? Il le sera au moyen de linstruction rendre expr, o expr est une expression compatible avec le type indiqu dans len-tte de la dclaration de la fonction.

56

Chapitre 6 Procdures et fonctions

fonction F([<paramtres formels>]) : T ... rendre expr {expr dlivre un rsultat de type T} ... finfonc {F}

Le corps de la fonction peut contenir plusieurs instructions rendre, mais lexcution du premier rendre a pour effet dachever lexcution de la fonction, et de revenir au contexte dappel avec la valeur rsultat.

6.6

LOCALISATION

lintrieur dun sous-programme, il est possible de dclarer des variables ou des constantes pour dsigner de nouveaux objets. Ces objets sont dits locaux. Chaque fois que des objets nont de signication qu lintrieur dun sous-programme, ils devront tre dclars locaux au sous-programme. Les noms locaux doivent tre dclars entre len-tte du sous-programme et son corps. Dans la procdure q2degr, la variable et la constante doivent tre dclares locales cette procdure puisquelles ne sont utilises que dans cette procdure.
procdure q2degr(donnes a, b, c : rel ; rsultats r1, i1, r2, i2 : rel) {Antcdent : a=0, b et c rels, coefficients de lquation du second degr, ax2 +bx+c} {Consquent : (x-(r1+ii1)) (x-(r2+ii2)) = 0} {Rle: calcule les racines dune quation du second degr} constante = ? {dpend de la prcision des rels sur la machine} variable type rel ... finproc {q2degr}

La validit des objets locaux est le texte du sous-programme tout entier. Les objets locaux, en particulier les variables, dmarrent leur existence au moment de lappel du sousprogramme, et sont dtruites lors de lachvement du sous-programme. Leur dure de vie est gale celle de lexcution du sous-programme dans lequel ils sont dnis. Les objets qui sont dclars lintrieur du sous-programme sont dits non locaux. Sils sont dnis en dehors, ils sont dits globaux, et sils sont prdnis par le langage, ils sont globaux et standard. Les objets globaux et standard ont une dure de vie gale celle du programme tout entier. Un autre aspect de la validit des objets locaux concerne lutilisation de leur nom, ce quon appelle la visibilit des noms. Un nom dni dans un sous-programme est visible partout lintrieur du sous-programme et invisible lextrieur. Ainsi la constante et la variable sont visibles partout dans la procdure q2degr et invisibles, i.e. inaccessibles lextrieur. Les objets globaux et standard sont visibles en tout point du programme. Certains langages autorisent la dclaration de sous-programmes locaux dautres sousprogrammes. Par exemple, on peut dclarer une procdure locale une autre pour effectuer

6.6

Localisation

57

un calcul particulier, et qui na de sens que dans cette procdure. On parle alors de sousprogrammes embots.
procdure P1 {Antcdent: ...} {Consquent: ...} {Rle: ...} procdure P2 {Antcdent: ...} {Consquent: ...} {Rle: ...} {corps de P2} ... finproc {P2} {corps de P1} ... P2 ... finproc {P1}

Nous venons de dire quun nom dni dans un sous-programme est visible partout lintrieur du sous-programme. Cette afrmation doit tre module. Imaginons que la procdure P1 prcdente possde une variable locale x. Est-il possible ou non de dclarer dans P2 une variable (et de faon gnrale un nom dobjet) du mme nom que x ? La rponse est oui.
procdure P1 {Antcdent : ...} {Consquent : ...} {Rle: ...} variable x type T procdure P2 {Antcdent : ...} {Consquent : ...} {Rle: ...} constante x = 1234 {procdure locale P1} ... x ... {la constante x de P2} finproc {P2} {corps de P1} P2 ... x ... {la variable x de P1} finproc {P1}

On peut alors prciser notre rgle : un nom dni dans un sous-programme est uniquement visible lintrieur du sous-programme. Cette visibilit peut tre limite par tout homonyme dclar dans un sous-programme embot.

58

Chapitre 6 Procdures et fonctions

lintrieur dun sous-programme, on peut donc manipuler des objets locaux et non locaux, ainsi que les paramtres. La manipulation directe des objets non locaux, en particulier globaux, qui appartiennent lenvironnement du sous-programme, est considre comme dangereuse puisquelle peut se faire depuis nimporte quel point du programme sans contrle. Une telle action sappelle un effet de bord et devra tre vite. Rgle Nous adopterons la discipline suivante : on communique avec la procdure en entre grce aux paramtres donnes et en sortie grce aux paramtres rsultats. Les noncs, lintrieur du sous-programme, ne manipuleront que des objets locaux ou des paramtres formels. Cette rgle correspond bien la vision dun sous-programme : une bote noire qui calcule, partir de donnes, des rsultats. Linterface entre le contexte dappel du sous-programme et lintrieur du sous-programme est donne par len-tte du sous-programme, cest--dire les paramtres donnes et rsultats.

6.7

RGLES DE DDUCTION

Les rgles de dduction pour un sous-programme seront bien sr fonction des noncs particuliers contenus dans son corps. Mais on peut dire que pour :
procdure P(donne pf1 : T1 rsultat pf2 : T2) {Antcdent : A = affirmation sur pf1} {Consquent : C = affirmation sur pf1 et pf2}

Lantcdent A de P est une afrmation sur la donne pf1. Le consquent C de P est une afrmation sur la donne pf1 et le rsultat pf2. Rgle 1 Dune faon gnrale, lantcdent dun sous-programme est une afrmation sur lensemble des paramtres formels donnes (et ceux la fois donnes et rsultats, sils existent). Le consquent est une afrmation sur lensemble des paramtres donnes et des paramtres rsultats (et ceux la fois donnes et rsultats sils existent). Rgle 2 Les deux afrmations A et C ne doivent contenir aucune rfrence aux paramtres effectifs. Rgle 3 Aucun objet local ne doit apparatre dans lantcdent ou le consquent. On peut constater que ces trois rgles sont bien vries dans lantcdent et le consquent de la procdure q2degr.

6.8

Exemples

59

procdure q2degr(donnes a, b, c : rel rsultats r1, i1, r2, i2: rel) {Antcdent : a=0, b et c rels, coefficients de lquation du second degr, ax2 +bx+c} {Consquent : (x-(r1+ii1)) (x-(r2+ii2)) = 0}

Lors dun appel de la procdure P avec les paramtres effectifs pe1 et pe2 :
{Ape1 } pf 1 P(pe1,pe2) {Cpe1,pe2 } pf 1,f p2

Lantcdent de lappel de la procdure P est lafrmation A dans laquelle pf1 a t remplac par pe1 ; et son consquent est lafrmation C dans laquelle pf1 et pf2 ont t remplacs, respectivement, par pe1 et pe2. Rgle 4 Dune faon gnrale, lantcdent de lappel dun sous-programme est lantcdent du sous-programme dans lequel lensemble des paramtres formels donnes (et ceux la fois donnes et rsultats, sils existent) ont t remplacs par les paramtres effectifs donnes (et ceux la fois donnes et rsultats, sils existent). Le consquent de lappel est le consquent du sous-programme dans lequel lensemble des paramtres formels donnes et rsultats (et ceux la fois donnes et rsultats, sils existent) ont t remplacs par les paramtres effectifs donnes et rsultats (et ceux la fois donnes et rsultats, sils existent). Rgle 5 Aprs remplacement des paramtres formels par les paramtres effectifs, les afrmations ne doivent plus contenir de rfrences des paramtres formels. En appliquant les rgles prcdentes, lantcdent et le consquent de lappel de la procdure q2degr de la page 54 sont :
{Antcdent : u=0, 1 et v+1 rels, coefficients de lquation du second degr, ux2 +x+v+1} q2degr(u,1,v+1,rl1,ig1,rl2,ig2) {Consquent : (x-(rl1+iig1)) (x-(rl2+iig2)) = 0}

6.8

EXEMPLES

Lcriture de lalgorithme de la page 49 dans lequel le calcul des racines est fait par une procdure est rcrit de la faon suivante :
Algorithme quation second degr {Antcdent : coeff1=0, coeff2 et coeff3 rels, coefficients dune quation du second degr} {Consquent : (x-(pre1+iimag1)) (x-(prel2+iimag2)) = 0} variables {les trois coefficients de lquation} coeff1, coeff2, coeff3, {les deux racines} prel1, prel2, pimag1, pimag2 type rel

60

Chapitre 6 Procdures et fonctions

{lire les 3 coefficients de lquation} lire(coeff1,coeff2,coeff3) {coeff1, coeff2, coeff3 coefficients rels de lquation coeff3x2 +coeff2x+coeff3=0 et coeff1=0} q2degr(coeff1,coeff2,coeff3,prel1,pimag1,prel2,pimag2) {(x-(prel1+ipimag1)) (x-(prel2+ipimag2)) = 0} {crire les rsultats} crire(prel1,pimag1,prel2,pimag2)

procdure q2degr(donnes a, b, c : rel rsultats r1, i1, r2, i2: rel) {Antcdent: a, b, c coefficients rels de lquation ax2 +bx+c=0 avec a=0} {Consquent: (x-(r1+ii1)) (x-(r2+ii2)) = 0} {Rle: calcule les racines dune quation du second degr} constante = ? {dpend de la prcision des rels sur la machine} variable type rel {le discriminant}

carr(b)-4ac si =0 alors {calcul des racines relles} si b>0 alors r1 -(b+ )/(2a) sinon r1 ( -b)/(2a) finsi {r1 est la racine la plus grande en valeur absolue} si |r1|< alors r2 0 sinon r2 c/(ar1) finsi i1 0 i2 0 {(x-r1)(x-r2)=0} sinon {calcul des racines complexes} r1 r2 -b/(2a) p ()/(2a) i1 i2 -i1 finsi {(x-(r1+ii1)) (x-(r2+ii2)) = 0} finproc {q2degr}

On dsire maintenant crire un programme qui afche la date du lendemain, avec le mois en toutes lettres. La date du jour est reprsente par trois entiers : jour, mois et anne. Le calcul de la date du lendemain ne pourra se faire que sur une date valide dont lanne est postrieure 15822 . Ainsi, si les donnes sont 31, 12, 2010 le programme afchera 1 janvier 2011.
2 Date

de ladoption du calendrier grgorien.

6.8

Exemples

61

Algorithme Date du lendemain {Antcdent : trois entiers qui reprsentent une date} {Consquent : la date du lendemain, si elle existe, est affiche} lire(jour, mois, anne) vrifier si la date est valide si non valide alors signaler lerreur sinon calculer la date du lendemain afficher la date du lendemain finsi

Le calcul de la date du lendemain ncessite la connaissance du nombre de jours maximum dans le mois. La vrication de la date dterminera cette valeur. La vrication de la date sera reprsente par la procdure valide dont len-tte est le suivant :
procdure valide(donnes j, m, a: entier rsultats ava: boolen max: entier) {Antcdent: j,m,a trois entiers quelconques qui reprsentent une date jour/mois/anne valide ou non} {Consquent: date valide ava=vrai et max=nombre de jours max du mois date non valide ava=faux et max est indtermin}

Lalgorithme principal avec les dclarations de variables scrit alors :


Algorithme Date du lendemain {Rle : lit sur lentre standard une date donne sous la forme de trois entiers correspondant au jour, au mois et lanne, et crit sur la sortie standard la date du lendemain si elle existe. Lanne doit tre suprieure une certaine date minimum et le jour doit correspondre un jour existant. Le mois est crit en toutes lettres.} constante anneMin = 1582 variables jour, mois, anne, nbreJours type entier ok type boolen
lire(jour, mois, anne) valide(jour, mois, anne, ok, nbreJours) si non ok alors crire("la date donne na pas de lendemain") sinon {la date est bonne on cherche son lendemain} {jour nbreJours et mois dans [1,12]} si jour < nbreJours alors {le mois et lanne ne changent pas} jour jour+1 sinon {cest le dernier jour du mois, il faut passer au

62

Chapitre 6 Procdures et fonctions

premier jour du mois suivant} jour 1 si mois<12 alors mois mois+1 sinon {cest le dernier mois de lanne, il faut passer au premier mois de lanne suivante} mois 1 anne anne+1 finsi finsi {crire la date du lendemain} crireDate(jour, mois, anne) finsi

Rappelons quune anne est bissextile si elle est divisible par 4, mais pas par 100, ou bien si elle est divisible par 400. Lanne 1900 nest pas bissextile alors que 2000 lest. Notez que la fonction bissextile est locale la procdure valide. Elle na de signication que dans cette procdure. La procdure valide scrit :
procdure valide(donnes j, m, a: entier rsultats ava: boolen max: entier) {Antcdent: j,m,a trois entiers quelconques qui reprsentent une date jour/mois/anne valide ou non} {Consquent: date valide ava=vrai et max=nombre de jours max du mois date non valide ava=faux et max est indtermin} fonction bissextile(donne anne : entier) : boolen {Antcdent: anne 1600} {Consquent: bissextile = vrai si lanne est bissextile faux sinon} rendre (anne modulo 4 = 0 et anne modulo 100 = 0) ou (anne modulo 400 = 0) finfonc {bissextile} {corps de la procdure valide} si a < anneMin alors ava faux sinon {lanne est bonne} si (m<1) ou (m>12) alors ava faux sinon {le mois est bon, tester le jour} choix m parmi 1, 3, 5, 7, 8, 10, 12 : max 31 4, 6, 9, 11 : max 30 2 : si bissextile(a) alors max 29 sinon max 28 finsi finchoix {max = nombre du jour dans le mois m} ava (j 1) et (j max) finsi finsi finproc {valide}

6.8

Exemples

63

Enn, crivons la procdure crireDate :


procdure crireDate(donnes j, m, a : entier) {Antcdent: j, m, a reprsentent une date valide} {Consquent: la date est crite sur la sortie standard avec le mois en toutes lettres} crire(j, ) choix m parmi 1 : crire("janvier") 2 : crire("fvrier") 3 : crire("mars") 4 : crire("avril") 5 : crire("mai") 6 : crire("juin") 7 : crire("juillet") 8 : crire("aot") 9 : crire("septembre") 10 : crire("octobre") 11 : crire("novembre") 12 : crire("dcembre") finchoix crire( , a) finproc {crireDate}

Chapitre 7

Programmation par objets

La programmation par objets est une mthodologie qui fonde la structure des programmes autour des objets. Dans ce chapitre, nous prsenterons les lments de base dun langage de classe : objets, classes, instances et mthodes.

7.1

OBJETS ET CLASSES

Nous avons vu quun objet lmentaire nest accessible que dans sa totalit. Un objet structur est, par opposition, construit partir dun ensemble ni de composants, accessibles indpendamment les uns des autres. Dans le monde de la programmation par objets, un programme est un systme dinteraction dune collection dobjets dynamiques. Selon B. M EYER [Mey88, Mey97], chaque objet peut tre considr comme un fournisseur de services utiliss par dautres objets, les clients. Notez que chaque objet peut tre la fois fournisseur et client. Un programme est ainsi vu comme un ensemble de relations contractuelles entre fournisseurs de services et clients. Les services offerts par les objets sont, dune part, des donnes, de type lmentaire ou structur, que nous appellerons des attributs, et dautre part, des actions que nous appellerons mthodes. Par exemple, un rectangle est un objet caractris par deux attributs, sa largeur et sa longueur, et des mthodes de calcul de sa surface ou de son primtre. Les langages de programmation par objets offrent des moyens de description des objets manipuls par le programme. Plutt que de dcrire individuellement chaque objet, ils fournissent une construction, la classe, qui dcrit un ensemble dobjets possdant les mmes proprits. Une classe comportera en particulier la dclaration des donnes et des mthodes. Les langages de programmation objets qui possdent le concept de classe sont appels langages de classes.

66

Chapitre 7 Programmation par objets

La dclaration dune classe correspond la dclaration dun nouveau type. Lensemble des rectangles pourra tre dcrit par une dclaration de classe comprenant deux attributs de type rel :
classe Rectangle largeur, longueur type rel finclasse Rectangle

La classe Rectangle fournira comme service ses clients, laccs ses attributs : sa longueur et sa largeur. Nous verrons dans la section 7.2 comment dclarer les mthodes. La dclaration dune variable r de type Rectangle scrit de la faon habituelle :
variable r type Rectangle

7.1.1

Cration des objets

Il est important de bien comprendre la diffrence entre les notions de classe et dobjet1 . Pour nous, les classes sont des descriptions purement statiques densembles possibles dobjets. Leur rle est de dnir de nouveaux types. En revanche, les objets sont des instances particulires dune classe. Les classes sont un peu comme des moules de fabrication dont sont issus les objets. En cours dexcution dun programme, seuls les objets existent. La dclaration dune variable dune classe donne ne cre pas lobjet. Lobjet, instance de la classe, doit tre explicitement cr grce loprateur crer. Chaque objet cr possde tous les attributs de la classe dont il est issu. Chaque objet possde donc ses propres attributs, distincts des attributs dun autre objet : deux rectangles ont chacun une largeur et une longueur qui leur est propre. Les attributs de lobjet prennent des valeurs initiales donnes par un constructeur. Pour chaque classe, il existe un constructeur par dfaut qui initialise les attributs des valeurs initiales par dfaut. Ainsi la dclaration suivante :
variable r type Rectangle crer Rectangle()

dnit une variable r qui dsigne un objet cr de type Rectangle, et dont les attributs sont initialiss la valeur relle 0 par le constructeur Rectangle(). La gure 7.1 montre une instance de la classe Rectangle avec ses deux attributs initialiss 0. La variable r qui dsigne lobjet cr est une rfrence lobjet, et non pas lobjet lui-mme.

Rectangle r largeur = 0.0 longueur = 0.0

F IG . 7.1 r dsigne un objet de type Rectangle.


1 Dans la plupart des langages objets, cette diffrence est plus subtile, puisquune classe peut tre elle-mme un objet.

7.1

Objets et classes

67

Le fonctionnement dune affectation dobjets de type classe nest plus tout fait le mme que pour les objets lmentaires. Ainsi, laffectation q r, affecte q la rfrence lobjet dsign par r. Aprs laffectation, les rfrences r et q dsignent le mme objet (voir la gure 7.2).

Rectangle r largeur = 0.0 longueur = 0.0 q

F IG . 7.2 r et q dsignent le mme objet.

7.1.2

Destruction des objets

Que faire des objets qui ne sont plus utiliss ? Ces objets occupent inutilement de la place en mmoire, et il convient de la rcuprer. Selon les langages de programmation, cette tche est laisse au programmeur ou traite automatiquement par le support dexcution du langage. La premire approche est dangereuse dans la mesure o elle laisse au programmeur la responsabilit dune tche complexe qui pose des problmes de scurit. Le programmeur est-il bien sr que lobjet quil dtruit nest plus utilis ? Au contraire, la seconde approche simpliera lcriture des programmes et offrira bien videmment plus de scurit. Notre notation algorithmique ne tiendra pas compte de ces problmes de gestion de la mmoire et aucune primitive ne sera dnie.

7.1.3

Accs aux attributs

Laccs un attribut dun objet est valide si ce dernier existe, cest--dire sil a t cr au pralable. Cet accs se fait simplement en le nommant. Dans une classe, toute rfrence aux attributs dnis dans la classe elle-mme sapplique linstance courante de lobjet, que nous appellerons lobjet courant. En revanche, laccs aux attributs depuis des clients, i.e. dautres objets, se fait par une notation pointe de la forme n.a, o n est un nom qui dsigne lobjet, et a un attribut particulier. La liste des services fournis aux clients est contrle explicitement par chaque objet. Par dfaut, nous considrerons que tous les attributs dune classe sont privs, et ne sont pas directement accessibles. Les attributs accessibles par les clients seront explicitement nomms dans une clause public. Pour rendre les deux attributs de la classe Rectangle publics, nous crirons :
classe Rectangle public largeur, longueur largeur, longueur type rel finclasse Rectangle

68

Chapitre 7 Programmation par objets

Ainsi, dans cette classe, la notation largeur dsigne lattribut de mme nom de lobjet courant. La notation r.largeur dsigne lattribut largeur de lobjet dsign par la variable r dclare prcdemment.

7.1.4

Attributs de classe partags

Nous avons vu que chaque objet possde ses propres attributs. Toutefois, il peut tre intressant de partager un attribut de classe par toutes les instances dune classe. Nous appellerons attribut partag, un attribut qui possde cette proprit. Un tel attribut possdera une information reprsentative de classe tout entire. Le mot-cl partag devra explicitement prcder la dclaration des attributs partags.

7.1.5

Les classes en Java

La syntaxe des classes en JAVA suit celle des classes donnes ci-dessus dans notre pseudolangage. La classe Rectangle scrira :
class Rectangle { public double largeur, longueur; }

La cration dun objet se fait grce loprateur new et son initialisation grce un constructeur dont le nom est celui de la classe. La variable r prcdente est dclare et initialise en JAVA de la faon suivante :
Rectangle r = new Rectangle();

La gestion de la destruction des objets est laisse la charge de linterprte du langage JAVA. Les objets qui deviennent inutiles, parce quils ne sont plus utiliss, sont automatiquement dtruits par le support dexcution. Toutefois, il est possible de dnir dans chaque classe la mthode finalize, appele immdiatement avant la destruction de lobjet, an dexcuter des actions dachvement particulires. Laccs aux attributs dun objet, appels membres dans la terminologie JAVA, se fait par la notation pointe vue prcdemment. Lautorisation daccs un membre par des clients est explicite grce au mot-cl public ou private2 plac devant sa dclaration. Par convention, lobjet courant est dsign par le nom this. Ainsi, les deux notations largeur et this.largeur dsignent le membre largeur de lobjet courant. On peut considrer que chaque objet possde un attribut this qui est une rfrence sur lui-mme. Un membre prcd du mot-cl static le rend partag par tous les objets de la classe. On peut accder un membre static mme si aucun objet na t cr, en utilisant dans la notation pointe le nom de classe. Ainsi, dans linstruction System.out.println(), System est le nom dune classe dans laquelle est dclare la variable statique out.
2 En fait, JAVA dnit deux autres types daccs aux membres dune classe, mais nous nen parlerons pas pour linstant.

7.2

Les mthodes

69

7.2

LES MTHODES

Le second type de service offert par un objet ses clients est un ensemble doprations sur les attributs. Le rle de ces oprations, appeles mthodes, est de donner le modle oprationnel de lobjet. Nous distinguerons deux types de mthodes, les procdures et les fonctions. Leurs dclarations et les rgles de transmission des paramtres suivent les mmes rgles que celles nonces au chapitre 6. Toutefois, nous considrerons, dune part, que les procdures ralisent une action sur ltat de lobjet, en modiant les valeurs des attributs de lobjet, et dautre part, que les fonctions se limitent renvoyer un tat de lobjet. Les procdures modieront directement les attributs de lobjet sans utiliser de paramtres rsultats. Notez que ce modle ne pourra pas toujours tre suivi la lettre. Certaines fonctions pourront tre amenes modier des attributs, et des procdures ne pas modier ltat de lobjet. Pour toutes les instances dune mme classe, il ny a quun exemplaire de chaque mthode de la classe. Contrairement aux attributs dinstance qui sont propres chaque objet, les objets dune classe partagent la mme mthode dinstance. Nous pouvons maintenant complter notre classe Rectangle avec, par exemple, deux fonctions qui renvoient le primtre et la surface du rectangle, et deux procdures qui modient respectivement la largeur et la longueur du rectangle courant. Notez que la dnition de ces deux dernires mthodes nest utile que si les attributs sont privs.
classe Rectangle public primtre, surface, changerLargeur, changerLongueur {les attributs} largeur, longueur type rel {les mthodes} fonction primtre() : rel {Rle: renvoie le primtre du rectangle} rendre 2(largeur+longueur) finfonc {primtre} fonction surface() : rel {Rle: renvoie la surface du rectangle} rendre largeurlongueur finfonc {surface} procdure changerLargeur(donne lg : rel) {Rle: met la largeur du rectangle lg} largeur lg finproc procdure changerLongueur(donne lg : rel) {Rle: met la longueur du rectangle lg} longueur lg finproc finclasse Rectangle

70

Chapitre 7 Programmation par objets

Si les mthodes possdent des en-ttes diffrents, bien quelles aient le mme nom, elles sont considres comme distinctes et dites surcharges. La notion de surcharge est normalement utilise lorsquil sagit de dnir plusieurs mises en uvre dune mme opration. La plupart des langages de programmation la propose implicitement pour certains oprateurs ; traditionnellement, loprateur + dsigne laddition entire et laddition relle, ou encore la concatnation de chanes de caractres, comme en JAVA. En revanche, peu de langages proposent aux programmeurs la surcharge des fonctions ou des procdures. Le langage E IFFEL linterdit mme, arguant que donner aux programmeurs la possibilit du choix dun mme nom pour deux oprations diffrentes est une source de confusion. Dans une classe, chaque proprit doit possder un nom unique3 .

7.2.1

Accs aux mthodes

Laccs aux mthodes suit les mmes rgles que celles aux attributs. Dans la classe, toute rfrence une mthode sapplique linstance courante de lobjet, et depuis un client il faudra utiliser la notation pointe. Pour rendre les mthodes accessibles aux clients, il faudra, comme pour les attributs, les dsigner publiques.
r.changerLargeur(2.5) r.changerLongueur(7) {r dsigne un rectangle de largeur 2.5 et de longueur 7}

7.2.2

Constructeurs

Nous avons dj vu que lors de la cration dun objet, les attributs taient initialiss des valeurs par dfaut, grce un constructeur par dfaut. Mais, il peut tre utile et ncessaire pour une classe de proposer son propre constructeur. Il est ainsi possible de rednir le constructeur par dfaut de la classe Rectangle pour initialiser les attributs des valeurs autres que zro.
classe Rectangle public primtre, surface, changerLargeur, changerLongueur {les attributs} largeur, longueur type rel constructeur Rectangle() largeur 1 longueur 1 fincons {les mthodes} ... finclasse Rectangle

Bien souvent, il est souhaitable de proposer plusieurs constructeurs aux utilisateurs dune classe. Malgr les remarques prcdentes, nous considrerons que toutes les classes peuvent dnir un ou plusieurs constructeurs, dont les noms sont celui de la classe, selon le mcanisme de surcharge. La classe Rectangle pourra dnir, par exemple, un second constructeur
3 Lire

avec intrt les arguments de B. M EYER [Mey97] contre le mcanisme de surcharge.

7.2

Les mthodes

71

pour permettre aux clients de choisir leurs propres valeurs initiales. Les paramtres dun constructeur sont implicitement des paramtres donnes .
constructeur Rectangle(donnes lar, lon : rel) largeur lar longueur lon fincons

Les crations de rectangles suivantes utilisent les deux constructeurs dnis prcdemment :
variables r type Rectangle crer Rectangle() {r dsigne un rectangle (1,1)} s type Rectangle crer Rectangle(3,2) {s dsigne un rectangle (3,2)}

7.2.3

Les mthodes en Java

La dclaration de la classe Rectangle scrit en JAVA comme suit :


class Rectangle { public double largeur, longueur; // les constructeurs public Rectangle() { largeur = longueur = 1; } public Rectangle(double lar, double lon) { largeur = lar; longueur = lon; } // les mthodes public double primtre() { // Rle : renvoie le primtre du rectangle return 2*(largeur+longueur); } public double surface() { // Rle : renvoie la surface du rectangle return largeur*longueur; } public void changerLargeur(double lg) { // Rle : met la largeur du rectangle lg largeur = lg; } public void changerLongueur(double lg) { // Rle : met la longueur du rectangle lg longueur = lg; } } // fin classe Rectangle

Les dclarations des mthodes dbutent par le type du rsultat renvoy pour les fonctions, ou par void pour les procdures. Suit le nom de la mthode et ses paramtres formels pla-

72

Chapitre 7 Programmation par objets

cs entre parenthses. Les noms des paramtres sont prcds de leur type, comme pour les attributs, et spars par des virgules. La transmission des paramtres se fait toujours par valeur. Mais, prcisons que dans le cas dun objet non lmentaire (i.e. dni par une classe), la valeur transmise est la rfrence lobjet, et non pas lobjet lui-mme. La mthode changerLongueur est une procdure un paramtre transmis par valeur, et la mthode primtre est une fonction sans paramtre qui renvoie un rel double prcision. Le corps dune mthode est parenths par des accolades ouvrantes et fermantes. Il contient une suite de dclarations locales et dinstructions. Dans une fonction, linstruction return e; termine lexcution de la fonction, et renvoie le rsultat de lvaluation de lexpression e. Le type de lexpression e doit tre compatible avec le type de valeur de retour dclar dans len-tte de la fonction. Une procdure peut galement excuter linstruction return, mais sans valuer dexpression. Son effet sera simplement de terminer lexcution de la procdure. Les dclarations des constructeurs suivent les rgles prcdentes, mais le nom du constructeur nest prcd daucun type, puisque cest lui-mme un nom de type (i.e. celui de la classe). Notez que le constructeur de lobjet courant est dsign par this(). Les mthodes et les constructeurs peuvent tre surchargs dans une mme classe. Leur distinction est faite sur le nombre et le type des paramtres, mais, attention, pas sur celui du type du rsultat. JAVA permet de dclarer une mthode statique en faisant prcder sa dclaration par le mot-cl static. Comme pour les attributs, les mthodes statiques existent indpendamment de la cration des objets, et sont accessibles en utilisant dans la notation pointe le nom de classe. La mthode main est un exemple de mthode statique. Elle doit tre dclare statique puisquaucun objet de la classe qui la contient nest cr. Notez que la notion de mthode statique remet en cause le modle objet, puisquil est alors possible dcrire un programme JAVA sans cration dobjet, et exclusivement structur autour des actions.

7.3

ASSERTIONS SUR LES CLASSES

Nous avons dj dit que la validit des programmes se dmontre de faon formelle, laide dassertions. Les assertions sur les actions sont des afrmations sur ltat du programme avant et aprs leur excution. De mme, il faudra donner des assertions pour dcrire les proprits des objets. B. M EYER4 nomme ces assertions des invariants de classe. Un invariant de classe est un ensemble dafrmations, mettant en jeu les attributs et les mthodes publiques de la classe, qui dcrit les proprits de lobjet. Linvariant de classe doit tre vri : aprs lappel dun constructeur ; avant et aprs lexcution dune mthode publique. Dans la mesure o les dimensions de rectangle doivent tre positives, un invariant possible pour la classe Rectangle est :
4 ibidem.

7.4

Exemples

73

{largeur

et longueur

0}

Le modle de programmation contractuelle tablit une relation entre une classe et ses clients, qui exprime les droits et les devoirs de chaque partie. Ainsi, une classe peut dire ses clients : Mon invariant de classe est vri. Si vous me promettez de respecter lantcdent de la mthode m que vous dsirez appeler, je promets de fournir un tat nal conforme linvariant de classe et au consquent de m. Si vous respectez lantcdent du constructeur, je promets de crer un objet qui satisfait linvariant de classe. Si ce contrat pass entre les classes et les clients est respect, nous pourrons garantir la validit du programme, cest--dire quil est conforme ses spcications. Toutefois, deux questions se posent. Comment vrier que le contrat est effectivement respect ? Et que se passe-t-il si le contrat nest pas respect ? La vrication doit se faire de faon automatique, le langage de programmation devant offrir des mcanismes pour exprimer les assertions et les vrier. Il est noter que trs peu de langages de programmation offrent cette possibilit. Si le contrat nest pas respect5 , lexcution du programme provoquera une erreur. Toutefois, peut-on quand mme poursuivre lexcution du programme lorsque la rupture du contrat est avre, tout en garantissant la validit du programme ? Nous verrons au chapitre 13, comment la notion dexception apporte une solution ce problme.

7.4

EXEMPLES

Dans le chapitre prcdent, nous avons vu comment structurer en sous-programmes la rsolution dune quation du second degr et le calcul de la date du lendemain. Nous reprenons ces deux exemples en les rorganisant autour des objets.

7.4.1

quation du second degr

Lobjet central de ce problme est lquation. On considre que chaque quation porte ses racines. Ainsi, chaque objet de type q2Degr possde comme attributs les solutions de lquation, qui sont calcules par le constructeur. Ce constructeur possde trois paramtres qui sont les trois coefcients de lquation. La classe fournit une mthode pour afcher les solutions.
classe q2Degr {Invariant de classe: public affichersol ax2 +bx+c=0 avec a=0}

r1, r2, i1, i2 type rel constructeur q2Degr(donnes a, b, c : rel)


5 Si lon suppose que les classes sont justes, ce sont les antcdents des mthodes appeles qui ne seront pas respects.

74

Chapitre 7 Programmation par objets

{Antcdent : a=0} {Consquent : (x - (r1+ii1)) (x - (r2 +ii2)) = 0} rsoudre(a,b,c) fincons procdure rsoudre(donnes a, b, c : rel) {Antcdent: a, b, c coefficients rels de lquation ax2 +bx+c=0 et a=0} {Consquent: (x-(r1+ii1)) (x-(r2+ii2)) = 0} constante = ? {dpend de la prcision des rels sur la machine} variable type rel {le discriminant}

carr(b)-4ac si 0 alors {calcul des racines relles} si b>0 alors r1 -(b+ )/(2a) sinon r1 ( -b)/(2a) finsi {r1 est la racine la plus grande en valeur absolue} si |r1|< alors r2 0 sinon r2 c/(ar1) finsi i1 0 i2 0 {(x-r1)(x-r2)=0} sinon {calcul des racines complexes} r1 r2 -b/(2a) /(2a) i1 i2 -i1 finsi {(x-(r1+ii1)) (x-(r2+ii2)) = 0} finproc {rsoudre}
procdure affichersol() {Antcdent: (x-(r1+ii1)) (x-(r2+ii2)) = 0} {Consquent : les solutions (r1,i1) et (r2,i2) sont affiches sur la sortie standard} crire(r1,i1) crire(r2,i2) finproc finclasse q2Degr

La traduction en JAVA de cet algorithme est immdiate et ne pose aucune difcult :


class q2Degr { // Invariant de classe : ax2 +bx+c=0 avec a=0 private double r1, r2, i1, i2; public q2Degr(double a, double b, double c) // Antcdent : a=0 // Consquent : (x - (r1+ii1)) (x - (r2+ii2)) = 0 { rsoudre(a, b, c); }

7.4

Exemples

75

public void rsoudre(double a, double b, double c) // Antcdent : a, b, c coefficients rels de lquation // ax2 +bx+c=0 et avec a=0 // Consquent : (x-(r1+ii1)) (x-(r2+ii2)) = 0 { final double = 1E-100; final double = (b*b)-4*a*c; if (>=0) { // calcul des racines relles if (b>0) r1 = -(b+Math.sqrt())/(2*a); else r1 = (Math.sqrt()-b)/(2*a); // r1 est la racine la plus grande en valeur absolue r2 = Math.abs(r1) < ? 0 : c/(a*r1); i1=i2=0; // (x-r1)(x-r2)=0 } else { // calcul des racines complexes r1 = r2 = -b/(2*a); i1=Math.sqrt(-)/(2*a); i2=-i1; } // (x-(r1+iii1)) (x-(r2+ii2)) = 0 } public String toString() // Consquent : renvoie une chane de caractres // qui contient les deux racines { return " r1= ( " + r1 + " , " + i1 + " ) \ n" + " r2= ( " + r2 + " , " + i2 + " ) "; } } // fin classe q2Degr

Vous notez la prsence de la mthode toString qui permet la conversion dun objet q2Degr en une chane de caractres6 . Celle-ci peut tre utilise implicitement par certaines mthodes, comme par exemple, la procdure System.out.println qui naccepte en paramtre quune chane de caractres. Si le paramtre nest pas de type String, deux cas se prsentent : soit il est dun type de base, et alors il est converti implicitement ; soit le paramtre est un objet, et alors la mthode toString de lobjet est appele an dobtenir une reprsentation sous forme de chane de caractres de lobjet courant. Nous pouvons crire la classe Test, contenant la mthode main, pour tester la classe
q2Degr.
import java.io.*; class Test { public static void main (String[] args) throws IOException {
6 Une chane de caractres est un objet constitu par une suite de caractres. Cette notion est prsente la section 9.7 page 97.

76

Chapitre 7 Programmation par objets

q2Degr e = new q2Degr(StdInput.readDouble(), StdInput.readDouble(), StdInput.readlnDouble()); // crire les racines solutions sur la sortie standard System.out.println(e); } } // fin classe Test

La variable e dsigne un objet de type q2Degr. Les trois paramtres de lquation sont lus sur lentre standard et passs au constructeur la cration de lobjet. Lappel de la mthode println a pour effet dcrire sur la sortie standard les deux racines de lquation e.

7.4.2

Date du lendemain

Lobjet central du problme est bien videmment la date. Nous dnissons une classe
Date, dont les principaux attributs sont trois entiers qui correspondent au jour, au mois et

lanne. Cette classe offre ses clients les services de calcul de la date du lendemain et dafchage (en toutes lettres) de la date. Notez que la liste de services offerte par cette classe nest pas ge ; si ncessaire, elle pourra tre complte ultrieurement. Le calcul de la date du lendemain modie lobjet courant en ajoutant un jour la date courante. La vrication de la validit de la date sera faite par le constructeur au moment de la cration de lobjet. Ainsi, linvariant de classe afrme que la date courante, reprsente par les trois attributs jour, mois et anne, constitue une date valide suprieure ou gale une anne minima. Lattribut qui dnit cette anne minima est une constante publique.
classe Date {Invariant de classe: les attributs jour, mois et anne reprsentent une date valide anneMin} public demain, afficher, anneMin jour, mois, anne type entier constante anneMin = 1582 constructeur Date(donnes j, m, a : entier) {Antcdent:} {Consquent: jour = j, mois = m et anne = a reprsentent une date valide} ... fincons procdure demain() {Antcdent : jour, mois, anne reprsentent une date valide} {Consquent : jour, mois, anne reprsentent la date du lendemain} ... finproc procdure afficher() {Consquent : la date courante est affiche sur la sortie standard} ... finproc

7.4

Exemples

77

finclasse Date

Nous nallons pas rcrire les corps du constructeur et des deux mthodes dans la mesure o leurs algorithmes donns page 61 restent les mmes. Toutefois, le calcul de la date du lendemain ncessite de connatre le nombre de jours maximum dans le mois et de vrier si lanne est bissextile. Le nombre de jours maximum est obtenu par la fonction publique nbJoursDansMois. Enn, la fonction bissextile sera une mthode que lon pourra aussi dnir publique, puisque dun usage gnral. Voici, lcriture en JAVA de la classe Date.
/** * La classe Date reprsente des dates de la forme * jour mois anne. * Invariant de classe: les attributs jour, mois et anne * reprsentent une date valide >= anneMin * */ class Date { private static final int AnneMin = 1582; private int jour, mois, anne; /** Consquent: jour = j, mois = m et anne = a reprsentent une date valide * */ public Date(int j, int m, int a) { if (a < anneMin) { System.err.println("Anne i n c o r r e c t e "); System.exit(1); } anne = a; // lanne est bonne, tester le mois if (m<1 || m>12) { System.err.println("Mois i n c o r r e c t "); System.exit(1); } mois = m; // le mois est bon, tester le jour if (j<1 || j>nbJoursDansMois(mois)) { System.err.println(" Jour i n c o r r e c t "); System.exit(1); } // le jour est valide jour = j; } /** Antcdent : 1 m 12 * Rle : renvoie le nombre de jours du mois m */ public int nbJoursDansMois(int m) { assert 1<=m && m<=12; switch (m) { case 1 : ;

78

Chapitre 7 Programmation par objets

case 3 : ; case 5 : ; case 7 : ; case 8 : ; case 10 : ; case 12 : return 31; case 4 : ; case 6 : ; case 9 : ; case 11 : return 30; case 2 : return bissextile() ? 29 : 28; } return -1; } /** Antcdent : jour, mois, anne reprsentent une date valide * Consquent : jour, mois, anne reprsentent la date du lendemain */ public void demain() { assert jour<=nbJoursDansMois(mois) && 1<=mois && mois<=12 && anne>=annMin; // jour et mois dans [1,12] et anne>=anneMin if (jour<nbJoursDansMois(mois)) // le mois et lanne ne changent pas jour++; else { // cest le dernier jour du mois, il faut passer au // premier jour du mois suivant jour=1; if (mois<12) mois++; else { // cest le dernier mois de lanne, il faut passer // au premier mois de lanne suivante mois=1; anne++; } } } /** Rle : teste si lanne courante est bissextile ou non */ public boolean bissextile() { return anne%4 == 0 && anne%100 != 0 || anne%400 == 0; } /** Consquent : renvoie la date courante sous forme dune chane de caractres * */ public String toString() { String smois=""; switch (mois) { case 1 : smois = " j a n v i e r "; break; case 2 : smois = " f v r i e r "; break; case 3 : smois = "mars"; break;

7.5

Exercices

79

case case case case case case case case case

4 5 6 7 8 9 10 11 12

: : : : : : : : :

smois smois smois smois smois smois smois smois smois

= = = = = = = = =

" a v r i l "; "mai"; " j u i n "; " j u i l l e t "; "aot"; "septembre"; "octobre"; "novembre"; "dcembre";

break; break; break; break; break; break; break; break;

} return jour + " " + smois + " " + anne; } } // fin classe Date

Le constructeur Date vrie la validit de la date. Si la date est incorrecte, il se contente dcrire un message sur la sortie derreur et darrter brutalement le programme. La classe de test donne ci-dessous cre un objet de type Date. Le jour, le mois et lanne sont lus sur lentre standard. Elle calcule la date du lendemain et lafche.
import java.io.*; class DateduLendemain { public static void main (String[] args) throws IOException { Date d = new Date(StdInput.readInt(), StdInput.readInt(), StdInput.readlnInt()); d.demain(); System.out.println(d); } } // fin classe DateduLendemain

7.5

EXERCICES

Exercice 7.1. Ajoutez la classe q2Degr les fonctions premireRacine et secondeRacine qui renvoient, respectivement, la premire et la seconde racine de lquation. Exercice 7.2. Dnissez une classe Complexe, pour reprsenter les nombres de lensemble C. Un objet complexe aura deux attributs, une partie relle et une partie imaginaire. Vous dnirez un constructeur par dfaut qui initialisera les deux attributs zro, ainsi quun constructeur qui initialisera un nombre complexe partir de deux paramtres rels. Vous crirez la mthode toString qui donne une reprsentation dun nombre complexe sous la forme (r,i). Exercice 7.3. Utilisez votre classe Complexe pour reprsenter les racines solutions de lquation du second degr. Exercice 7.4. Compltez la classe Complexe avec les oprations daddition et de multiplication. Notez que la multiplication est plus simple crire si les complexes sont reprsents

80

Chapitre 7 Programmation par objets

sous forme polaire. On rappelle que tout complexe z admet une reprsentation cartsienne x + iy et polaire ei o est le module et largument de z. Largument nest dni que si z = 0. Le passage dun systme de coordonnes lautre se fait laide des formules de conversion : coordonnes polaires = x2 + y 2 coordonnes cartsiennes x = cos() y = sin()

= atan(y/x)

Le produit de deux complexes z1 et z2 est donn par les deux quations suivantes :

(z1 z2) = (z1) (z2) (z1 z2) = (z1) + (z2) Exercice 7.5. Compltez la classe Date avec les mthodes suivantes : hier qui soustrait un jour la date courante ; avant, aprs, gale qui teste si une date passe en paramtre est, respectivement, antrieure, postrieure ou gale la date courante. jourscouls qui renvoie le nombre de jours couls entre la date courante et une date passe en paramtre. nomDuJour qui renvoie le nom du jour de la semaine de la date courante.

Chapitre 8

noncs itratifs

Jusqu prsent, nous avons vu quun nonc ne pouvait tre excut que zro ou une fois. Est-il possible de rpter plus dune fois lexcution dun nonc ? La rponse est oui, grce aux noncs itratifs.

8.1

FORME GNRALE

Un nonc itratif, comme le montre sa reprsentation circulaire donne par la gure 8.1, est souvent appel boucle. On entre dans la boucle avec lantcdent Pa , et chaque tour de boucle les noncs qui la composent sont excuts. Larrt de la boucle se produira lorsque le prdicat dachvement B sera vri. Lafrmation Pa est lantcdent de la boucle, Pc le consquent, et P0 , P1 , ..., Pn des afrmations toujours vraies quel que soit le nombre ditrations. Ces dernires sont appeles invariants de boucle. La rgle de dduction de lnonc itratif sexprime comme suit :
si {P0 } {P1 } {P2 } ... {Pi1 } {Pi } {Pi+1 } ... {Pn1 } {Pn } et {Pn } {P0 } et {Pa } {P0 } et {Pi et B } {Pc } et {Pi et non B } {Pi+1 } alors {Pa } nonc-itratif-gnral {Pc }
Ei+1 E1 E2 Ei Ei+1 En

Lemplacement spcique de lafrmation Pi , i.e. juste avant le prdicat dachvement, lui confre un rle privilgi. On considrera cette afrmation comme linvariant de boucle. Cet invariant dcrit la smantique de lnonc itratif, et doit tre un pralable, ce qui nest pas toujours vident, la programmation de la boucle. Une syntaxe possible dun nonc itratif gnral est la suivante :

82

Chapitre 8 noncs itratifs

Pa

Pn En Pn1

P0 E1 P1

Pi+1 Ei+1 Pi et non B B Pi Ei

Pi1

Pc = P et B i
F IG . 8.1 Forme gnrale dun nonc itratif.

itrer E1 arrt si B E2 finitrer

Sa rgle de dduction est donne ci-dessous. Linvariant de boucle est lafrmation Q.


si {P } {Q} et {Q et non B} {P } alors {P } itrer E1 arrt si B E2 finitrer {Q et B }
E1 E2

partir de la forme gnrale de lnonc itratif, deux noncs particuliers peuvent tre dduits selon que lensemble des noncs de A1 Ai ou lensemble des noncs de Ai+1 An est vide. Ces deux noncs itratifs, qui existent dans la plupart des langages de programmation, sont les noncs tantque et rpter.

8.2

LNONC TANTQUE

Lorsque les noncs A1 Ai sont vides, nous sommes en prsence dun nonc tantque. Sa syntaxe est gnralement :
tantque B faire E fintantque

o le prdicat dachvement B est une expression boolenne. Tant que lvaluation de lexpression B donne la valeur vrai, lnonc E est excut. La boucle sachve lorsque B est faux. Si le prdicat dachvement est immdiatement vri, lnonc E nest pas excut. Lnonc E peut donc tre excut zro ou plusieurs fois. Plus formellement, la rgle de dduction de cet nonc tantque est :

8.3

Lnonc rpter

83

si {P et B} {P } alors {P } tantque B faire E fintantque {P et non B }

Lnonc tantque de J AVA Cet nonc scrit selon une syntaxe classique. Notez cependant, la prsence de parenthses autour du prdicat boolen B, et labsence de mot-cl avant lnonc E.
while (B) E

8.3

LNONC RPTER

Cette fois-ci, les noncs Ai+1 An sont vides. Lnonc itratif sappelle lnonc
rpter et sa syntaxe est la suivante :
rpter E jusqu B

Le prdicat dachvement B est l encore une expression boolenne. Lnonc E est excut jusqu ce que lvaluation de lexpression B donne la valeur vrai. Tant que la valeur est faux, lnonc E est excut. Notez que lnonc E est excut au moins une fois. Ainsi, le choix entre lutilisation dun nonc tantque et rpter dpendra du nombre ditrations minimum effectuer. Plus formellement, la rgle de dduction de lnonc rpter est donne ci-dessous. Notez que linvariant de boucle est lafrmation Q et non pas P !
si {P } E {Q} et {Q et non B } E {Q} alors {P } rpter E jusqu B {Q et B }

Un langage de programmation pourrait se limiter la seule dnition de lnonc itratif


tantque. En effet, tous les autres noncs itratifs peuvent sexprimer partir de celui-ci. Par exemple, les noncs itrer et rpter scrivent comme suit :
itrer E1 tantque non B faire E2 E1 fintantque rpter E tantque non B faire E fintantque

Lnonc rpter de J AVA Cet nonc scrit de la faon suivante :


do E while (B);

Notez que la smantique du prdicat dachvement est loppos de celle de la forme algorithmique. Litration sachve lorsque le prdicat B prend la valeur faux. La rgle de dduction est donc lgrement modie :
si {P } E {Q} et {Q et B } E {Q} alors {P } do E while (B); {Q et non B }

84

Chapitre 8 noncs itratifs

8.4

FINITUDE

Lorsquun programme excute une boucle, il faut tre certain que le processus itratif sachve ; sinon on dit que le programme boucle. La nitude des noncs itratifs est une proprit fondamentale des programmes informatiques. Lachvement dune boucle ne peut se vrier par son excution sur lordinateur. Dmontrer la nitude dune boucle se fait de faon analytique : on cherche une fonction f (X) o X reprsente des variables mises en jeu dans le corps de la boucle et qui devront ncessairement tre modies par le processus itratif. Cette fonction est positive et dcrot strictement vers une valeur particulire. Pour cette valeur, par exemple zro, on en dduit que B est vri, et que la boucle sachve.

8.5

EXEMPLES

La premire partie de ce chapitre tait assez thorique. Il est temps de montrer lutilisation des noncs rptitifs sur des exemples concrets.

8.5.1

Factorielle

Nous dsirons crire la fonction factorielle qui calcule la factorielle dun entier naturel pass en paramtre. Rappelons que la factorielle dun entier naturel n est gale : n! = 1 2 3 (i 1) i . . . n Une premire faon de procder est de calculer une suite croissante de produits de 1 n. Peut-on dterminer immdiatement linvariant de boucle ? Au ie produit, cest--dire la ie itration qua-t-on calcul ? Rponse i! et ce sera notre invariant. Lalgorithme et la dmonstration de sa validit sont donns ci-dessous :
fonction factorielle(donne n : naturel) : naturel {Antcdent : n 0} {Consquent : factorielle = n!} variables i, fact type naturel i 0 fact 1 {Invariant : fact = i!} tantque i<n faire {fact (i+1) = i! (i+1) et i<n} i i+1 {fact i = i!} fact facti {fact = i!} fintantque {i = n et fact = i! = n!} rendre fact finfonc {factorielle}

Une fonction qui permet de dmontrer la nitude de la boucle est f (i) = n i. Lorsque i = n, le prdicat dachvement est vri.

8.5

Exemples

85

8.5.2

Minimum et maximum

Soit une suite non vide dentiers, x1 , x2 , x3 , . . . xn , dont on dsire trouver le minimum et le maximum. Un entier de la suite est lu chaque itration, et le calcul du minimum et du maximum se fait progressivement. la ie itration, on afrme que k [1, i], min xk et max xk . Puisquil y a au moins un entier lire sur lentre standard, nous utiliserons un nonc rpter. Les valeurs initiales de min et max doivent tre telles que tout x1 est infrieur min et suprieur max.
procdure MinMax(donne n : naturel rsultats min, max : entier) {Antcdent : n > 0} {Consquent : min et max respectivement minimum et maximum dune suite dentiers lue sur lentre standard} variable i type [0,n] min + max i 0 rpter i i+1 lire(x) si x < min alors min {k [1, i], min xk } si x > max alors max {k [1, i], min xk et max jusqu i = n {k [1, i], min xk et max xk et finproc {MinMax}

x finsi x finsi xk } i=n}

Comme pour factorielle, la fonction f (i) = n i permet de dmontrer la nitude de la boucle.

8.5.3

Division entire

Rappelons tout dabord, la dnition de la division entire de deux entiers naturels a et b : a 0, b > 0, a = quotient b + reste , 0 r<b

Pour calculer la division entire, nous allons procder par soustractions successives de la valeur b la valeur a jusqu ce que le rsultat de la soustraction soit infrieur b. Daprs ce que nous venons de dire linvariant est la dnition mme de la division entire.
procdure DivisionEntire(donnes a, b : naturel rsultats quotient, reste : naturel) {Antcdent : a 0, b>0} {Consquent : a = quotientb + reste, 0 reste<b} reste a quotient 0 {Invariant : a = quotient b + reste}

86

Chapitre 8 noncs itratifs

tantque reste b faire {a = (quotient+1) b + reste - b et reste>b} quotient quotient+1 {a = quotient b + reste - b et reste b} reste reste-b {a = quotient b + reste} fintantque {a = quotient b + reste et 0 reste<b} finproc {DivisionEntire}

La fonction f (reste) = reste b garantit la nitude de la boucle.

8.5.4

Plus grand commun diviseur

Le plus grand commun diviseur de deux nombres naturels est lentier naturel le plus grand qui les divise tous les deux. Il est tel que : pgcd(a, b) = pgcd(a b, b) et a > b = pgcd(a, b a) et a < b Lalgorithme propos par E UCLIDE1 procde par soustractions successives des valeurs a et b, jusqu ce que a et b soient gaux.
fonction pgcd(donnes a, b : naturel) : naturel {Antcdent : a>0 et b>0} {Consquent : pgcd(a,b) = pgcd(a-b,b) et a>b = pgcd(a,b-a) et a<b} tantque a=b faire {pgcd(a,b) = pgcd(a-b,b) et a>b = pgcd(a,b-a) et a<b} si a>b alors {pgcd(a,b) = pgcd(a-b,b) et a>b} a a-b sinon b b-a {pgcd(a,b) = pgcd(a,b-a) et a<b} finsi fintantque {a = pgcd(a,b)} rendre a finfonc {pgcd}

Lapplication successive des fonctions f (a, b) = a b lorsque a > b et f (a, b) = b a lorsque a < b tend vers lgalit de a et de b. Le prdicat dachvement sera donc vri.
1 E UCLIDE ,

mathmaticien grec du IIIe sicle avant J.C.

8.5

Exemples

87

8.5.5

Multiplication

Lalgorithme de multiplication suivant procde par sommes successives. Le produit x y consiste sommer y fois la valeur x. Toutefois, on peut amliorer cet algorithme rudimentaire en multipliant x par deux et en divisant y par deux chaque fois que sa valeur est paire. Les oprations de multiplication et de division par deux sont des oprations trs efcaces puisquelles consistent dcaler un bit vers la gauche ou vers la droite.
fonction produit(donnes x, y : naturel): naturel {Antcdent : x = , y = } {Consquent : produit = x y = } variable p type naturel p 0 { = p + x y } tantque y>0 faire { = p + x y et y>0} tantque y est pair faire { = p + x y et y = (y/2) 2 > 0} { = p + 2x (y/2) et y = (y/2) 2 > 0} y y/2 { = p + 2x y} x 2x { = p + x y } fintantque { = p + (x-1) y + y et y>0 et y impair} p p+x { = p + x y-1 et y impair} y y-1 { = p + x y} fintantque {y = 0 et = p + x y = p} rendre p finfonc {produit}

La nitude de la boucle la plus interne est vidente. Les divisions entires successives par deux dun nombre pair tendent ncessairement vers un nombre impair. La boucle externe se termine bien galement puisque la fonction f (y) = y 1 fait tendre y vers 0 pour tout y > 0.

8.5.6 Puissance
Lalgorithme dlvation la puissance suit le mme principe que prcdemment. Le calcul de xy consiste faire y fois le produit x. De la mme faon, lorsque y est pair, x est lev au carr tandis que y est divis par deux.
fonction puissance(donnes x, y : naturel) : naturel {Antcdent : x = , y = } {Consquent : puissance = x y = } variable p type naturel

88

Chapitre 8 noncs itratifs

p 1 { = p xy } tantque y>0 faire { = p xy et y > 0} tantque y est pair faire { = p xy et y = (y/2) 2 > 0} y y/2 { = p x2y } x xx { = p x y } fintantque { = p xy et y impair} p px { = p xy-1 et y impair} y y-1 { = p x y } fintantque { = p xy et y=0 p = } rendre p finfonc {puissance}

La nitude des deux boucles se dmontre comme celle des deux boucles de fonction
produit.

8.6

EXERCICES

Exercice 8.1. Programmez en JAVA les fonctions donnes dans ce chapitre. Exercice 8.2. Le calcul de factorielle est galement possible en procdant de faon dcroissante. crivez une fonction factorielle selon cette mthode. Vous prouverez la validit de votre algorithme, et dmontrerez la nitude de la boucle. Exercice 8.3. Montrez formellement que dans lalgorithme de la recherche du minimum et du maximum dune suite dentiers donne la page 85, lutilisation de lnonc conditionnel suivant est faux.
si x < min alors min x sinon si x > max alors max x finsi finsi

Exercice 8.4. crivez une fonction qui calcule le sinus dune valeur relle positive daprs son dveloppement en srie entire :
+

sin(x) =
i=0

(1)i

x2i+1 x3 x5 x7 =x + + ... (2i + 1)! 3! 5! 7!

8.6

Exercices

89

Le calcul de la somme converge lorsquune prcision est atteinte, cest--dire lorsquun nouveau terme t est tel que |t| . Note : ne pas utiliser la fonction factorielle, ni la fonction puissance. Exercice 8.5. Les oprateurs << et >> du langage JAVA permettent de faire des dcalages binaires vers la gauche ou vers la droite. Loprande gauche est la conguration binaire dcaler, et loprande droit est le nombre de bits dcaler. Pour un dcalage vers la gauche, des zros sont systmatiquement rinjects par la droite. Pour un dcalage vers la droite, les bits rinjects gauche sont des uns si la conguration binaire est ngative, sinon ce sont des zros. Loprateur >>> dcale vers la droite et rinjecte exclusivement des zros. laide des oprateurs de dcalage binaire et de loprateur & (et logique), rcrivez le produit de deux entiers naturels x et y, ainsi que llvation de x la puissance y. Lalgorithme utilisera la dcomposition binaire de y. On rappelle que la multiplication par deux dun entier correspond un dcalage dun bit vers la gauche, et que la division par deux dun entier correspond un dcalage dun bit vers la droite. Dautre part, lopration x & 1 retourne la valeur du bit le plus droite de x. Exercice 8.6. Calculez pgcd(2015, 1680) laide de lalgorithme donn page 86. Combien ditrations sont ncessaires ce calcul ? Lalgorithme dE UCLIDE scrit galement sous la forme suivante :
fonction pgcd(donnes a, b : naturel) : naturel variable reste type naturel si a>b alors changer(a,b) finsi tantque b=0 faire reste a modulo b a b b reste fintantque rendre a finfonc {pgcd}

Combien ditrations ncessitent les calculs de pgcd(2015, 1680) et pgcd(6765, 10946). Prouvez la validit de cet algorithme. Comparez les deux critures de lalgorithme. Pensezvous que cette seconde version est plus efcace ? Exercice 8.7. Une fraction est reprsente par deux entiers naturels : le numrateur et le dnominateur. Dnissez une classe Fraction et un mthode irrductible qui rend irrductible la fraction courante. Exercice 8.8. Programmez la fonction boolenne estPremier qui teste si lentier naturel pass en paramtre est un nombre premier. On rappelle quun nombre est premier sil nadmet comme diviseur que 1 et lui-mme. En dautres termes, n est premier si le reste de la division entire de n par d nest jamais nul, d, 2 d n 1. Exercice 8.9. On dsire calculer la racine carre dun nombre rel x par la mthode de H RON LA NCIEN2 . Pour cela, on calcule la suite rn = (rn1 + x/rn1 )/2 jusqu obtenir
2 H RON LA NCIEN ,

mathmaticien et mcanicien grec du Ier sicle.

90

Chapitre 8 noncs itratifs

2 une approximation rn = x telle que |rn x| est infrieur un donn. Vous pourrez choisir r0 = x/2. crivez un algorithme itratif qui procde ce calcul. Exercice 8.10. En utilisant la suite rn = (2rn1 + x/(rn1 )2 )/3, crivez lalgorithme qui calcule la racine cubique dun entier naturel x.

Chapitre 9

Les tableaux

Les tableaux dnissent un nouveau mode de structuration des donnes. Un tableau est un agrgat de composants, objets lmentaires ou non, de mme type et dont laccs ses composants se fait par un indice calcul.

9.1

DCLARATION DUN TABLEAU

Dune faon gnrale, les composants dun tableau sont en nombre ni, et sont accessibles individuellement sans ordre particulier de faon directe grce un indice calcul. Cette dernire opration sappelle une indexation. La dnition dun tableau doit faire apparatre : le type des composants ; le type des indices. La dclaration dun tableau t possdera la syntaxe suivante :
t type tableau [T1 ] de T2

o T1 et T2 sont, respectivement, le type des indices et le type des composants. Il ny a aucune restriction sur le type des composants, alors que le type des indices doit tre discret. En gnral, tous les types simples sont autoriss (hormis le type rel) sur lesquels doit exister une relation dordre. Notez que cette dclaration dnit une application de T1 vers T2 . Un tableau dnit un ensemble de valeurs dont la cardinalit est gale | T2 ||T1 | , o | T | dsigne le nombre dlments du type T. La cardinalit du type T1 correspond au nombre de composants du tableau.

92

Chapitre 9 Les tableaux

Exemples de dclarations de tableaux


t1 type tableau [boolen] de entier t2 type tableau [ [1,10] ] de caractre

La variable t1 est un tableau deux composants de type entier. Le type des indices est le type boolen. La variable t2 est un tableau dix composants de type caractre. Le type des indices de t2 est le type intervalle [1,10]. La gure suivante montre une reprsentation de ces deux tableaux en mmoire. Les composants sont rangs de faon contigu. Chaque case est un composant particulier, dont le type est indiqu en italique et gauche la valeur de lindice qui permet dy accder. faux vrai t1 entier entier 1 2 3 4 5 6 7 8 9 10 t2 caractre caractre caractre caractre caractre caractre caractre caractre caractre caractre

Dans certains langages de programmation, comme C ou PASCAL, le nombre de composants dun tableau est constant et g par sa dclaration la compilation. Au contraire, dautres langages dnissent des tableaux dynamiques dont le nombre dlments peut varier lexcution.

9.2

DNOTATION DUN COMPOSANT DE TABLEAU

Les composants sont dnots au moyen du nom de la variable dsignant lobjet de type tableau et de lindice qui dsigne de faon unique le composant dsir. On dsignera un composant dun tableau t dindice i, par la notation t[i] (prononce t de i), qui est une expression fournissant comme valeur un objet du type des composants ; i peut tre une expression pourvu quelle fournisse une valeur du type des indices. La variable t est de type tableau alors que t[i] est du type des composants. Avec les dclarations prcdentes, les notations suivantes sont valides :
t1[vrai] t2[1] t1[faux] t2[10] t1[non vrai] t2[3+4] {de type entier} {de type caractre}

Il est important de bien comprendre que lindice doit renvoyer une valeur sur le type des indices, ce qui nest pas toujours facilement dcelable.
t2[23] t2[i+j]

9.3

Modication slective

93

La premire notation est manifestement fausse, alors que la seconde dpend des valeurs de
i et de j, qui risquent de ntre connues qu lexcution du programme. Donner un indice

hors de son domaine de dnition est une erreur de programmation. Selon les programmes, les langages et les compilateurs, cette erreur sera signale ds la compilation, lexcution, ou encore pas du tout. Cette dernire faon de procder est celle du langage C, rendant la construction de logiciels ables plus difcile.

9.3

MODIFICATION SLECTIVE

La notation t[i] dsigne un composant particulier du tableau t. On peut considrer que t[i] est un nom de variable, et nous utiliserons cette notation pour obtenir, et surtout pour modier de faon slective, cest--dire modier un composant indpendamment des autres, la valeur de lobjet quelle dsigne. Il sera alors possible dcrire :
t1[vrai] 234 t2[4] z t1[faux] 0 t2[i+5] a t1[non p et q] -13 t2[i-j] 0

9.4

OPRATIONS SUR LES TABLEAUX

Les langages de programmation noffrent, en gnral, que peu doprations prdnies sur les tableaux. Le plus souvent, ils proposent les oprations de comparaison, qui testent si deux tableaux sont strictement identiques ou diffrents, ainsi que laffectation. Deux tableaux sont gaux sils sont de mme type, et si tous leurs composants sont gaux. Laffectation affecte individuellement chaque composant du tableau, en partie droite de laffectation, aux composants correspondants du tableau en partie gauche.
variables t1, t2 type tableau[(bleu,rouge,vert)] de rel ... t2 t1 ... si t1 = t2 alors ...

9.5

LES TABLEAUX EN JAVA

Une dclaration de tableau possde la forme suivante :


Tc [] t;

o Tc est le type des composants du tableau t. Par exemple, les dclarations dun tableau dentiers t1 et de rectangles t2 scrivent :
int [] t1; // un tableau dentiers Rectangle [] t2; // un tableau de Rectangle

94

Chapitre 9 Les tableaux

Cette dclaration ne cre pas les composants du tableau. Remarquez quelle nindique pas non plus leur nombre. Elle dnit simplement une rfrence un objet de type tableau. La cration des composants du tableau se fait dynamiquement lexcution du programme. Celle-ci doit tre explicite laide de loprateur new qui en prcise leur nombre.
t1 = new int [ 10 ]; t2 = new Rectangle [ 5 ];

La premire instruction cre un tableau de dix lments de type int, et la seconde, un tableau de cinq lments de type Rectangle. Le nombre dlments dun tableau est x une fois pour toutes lors de sa cration et chaque instance de tableau possde un attribut, length, dont la valeur est gale au nombre dlments. Une autre faon de crer les composants dun tableau consiste numrer leurs valeurs :
// la dclaration int [] t1 = { 4, -5, 12 }; Rectangle [] t2 = { null, new Rectangle(3,5) }; // ou aprs la dclaration t1 = new int [] { 4, -5, 12 }; t2 = new Rectangle [] { null, new Rectangle(3,5) };

Le tableau t1 est form de trois composants dont les valeurs sont 4, -5 et 12. Le tableau t2 possde deux composants de type Rectangle, le premier est la rfrence null, le second est une rfrence sur une instance particulire (i.e. un rectangle de largeur 3 et de longueur 5). Le type des indices des tableaux est toujours un intervalle dentiers dont la borne infrieure est zro et la borne suprieure le nombre dlments moins un. Le premier lment, t[0], est lindice 0, le deuxime, t[1], lindice 1, etc. Le dernier lment est t[t.length-1]. Si un indice i nappartient pas au type des indices dun tableau t, lerreur produite par la notation t[i] sera signale lexcution par un message davertissement1 . la cration dun tableau, les lments de type numrique sont initialiss par dfaut zro, faux lorsquils sont de type boolen, et \u0000 pour le type caractre. Si ce sont des objets de type classe (ou des tableaux leur tour), les instances de classe de chaque lment du tableau ne sont pas cres. La valeur de chaque lment est gale la rfrence null. Dans la mesure o une variable de type tableau est une rfrence une instance de tableau, les instructions suivantes :
t1 = t2 t1 == t2

naffectent pas les lments de t2 t1 et ne comparent pas les lments des tableaux t1 t2. Au contraire, elles affectent et comparent les rfrences ces deux tableaux. Laffectation et la comparaison des lments devront tre programmes explicitement lment lment. La mthode clone de la classe Object et les mthodes copyOf, copyOfRange et equals de la classe java.util.Arrays aideront le programmeur dans cette tche. Notez que cette situation se produit galement lors dun passage dun tableau2 en paramtre. Seule la rfrence au tableau est transmise par valeur.
1 De 2 Et

la forme ArrayIndexOutOfBoundsException.

plus gnralement, pour toute instance dobjet.

9.6

Un exemple

95

9.6

UN EXEMPLE

Nous voulons initialiser de faon alatoire les composants dun tableau dentiers et rechercher dans le tableau loccurrence dune valeur entire particulire. Les dclarations suivantes dnissent un tableau tab n composants entiers.
constante n = 10 {nombre dlments du tableau} variable tab type tableau [ [1,n] ] de entier

Lalgorithme dinitialisation suivant utilise la fonction random qui renvoie un entier tir de faon alatoire.
Algorithme Initialisation alatoire {Rle : initialise le tableau tab de faon alatoire} variable i type entier i 0 rpter {i [1,i], tab[i] est initialis alatoirement} i i+1 tab[i] random() {random renvoie un nb alatoirement} jusqu i=n {i [1,n], tab[i] est initialis alatoirement}

Linitialisation du tableau est un parcours systmatique de tous les lments, du premier au dernier. Le nombre ditrations est donc connu lavance. Remarquez que les noncs itratifs rpter et tantque ne sont pas vraiment adapts puisquils rclament un prdicat dachvement. Nous verrons au prochain chapitre, comment lnonc itratif pour offre une notation simple pour lcriture dune telle boucle. Lalgorithme de recherche parcourt le tableau de faon linaire partir du premier lment. Si llment est trouv, il renvoie la valeur vrai, sinon il renvoie la valeur faux.
Algorithme Recherche linaire {Antcdent : x entier rechercher dans le tableau tab} {Consquent : renvoie x tab} variable i type entier i 1 rpter {i [1,i-1], x =tab[i]} si tab[i]=x {trouv} alors rendre vrai sinon i i+1 finsi jusqu i=n {i [1,n], x=tab[i]} rendre faux

96

Chapitre 9 Les tableaux

Programmation en J AVA Nous allons dnir une classe TabAla dont le constructeur se chargera dinitialiser le tableau. Puisque JAVA permet la construction dynamique des composants du tableau, le nombre dlments sera pass en paramtre au constructeur. La classe Random, dnie dans le paquetage java.util, permet de fabriquer des gnrateurs de valeurs entires ou relles calcules de faon pseudo-alatoire. La mthode nextInt renvoie le prochain entier.
import java.util.*; class TabAla { // Invariant de classe : TabAla reprsente une suite // alatoire de n entiers int [] tab; TabAla(int n) // Rle : crer les n composants du tableau tab // et initialiser tab de faon alatoire { // crer un gnrateur de nombres alatoires Random rand = new Random(); // crer les n composants du tableau tab = new int [n]; int i=0; do // i [0,i-1], tab[i] est initialis alatoirement tab[i++] = rand.nextInt(); while (i<n); // i [0,n-1], tab[i] est initialis alatoirement } boolean rechercher(int x) // Antcdent : x entier rechercher // Consquent : renvoie x tab { int i=0; do // i [0,i-1], x=tab[i] if (tab[i++]==x) // trouv return true; while (i<tab.length); // i [0,tab.length-1], x=tab[i] return false; } } // fin classe TabAla

Lexpression i++ incrmente la variable i de un, et renvoie comme rsultat la valeur de i avant lincrmentation. Noubliez pas quen JAVA une affectation est une expression. La comparaison (tab[i++] == x) compare donc tab[i] x avant lincrmentation de i. Notez que lexpression ++i renvoie comme rsultat la valeur de i aprs incrmentation.

9.7

Les chanes de caractres

97

9.7

LES CHANES DE CARACTRES

Dans les chapitres prcdents, nous avons dj manipul des chanes de caractres sans connatre exactement la nature de cet objet. Nous savons quune constante chane se dnote entre deux guillemets :
"toto" "lt" "bonjour tous"

Certains langages de programmation, comme PASCAL ou C par exemple, reprsentent les chanes de caractres par des tableaux de caractres. Dautres proposent, comme S NOBOL (voir page 10), un type prdni chane de caractres sans prciser de reprsentation particulire. Le nombre doprations prdnies sur les chanes varie considrablement dun langage lautre. Certains nen dnissent que trs peu, comme PASCAL par exemple, dautres, au contraire comme S NOBOL, en proposent une multitude. Outre la dnotation de constantes et la dclaration de variables, les oprations traditionnelles de manipulation de chanes sont la concatnation (i.e. la mise bout bout des chanes), le calcul de la longueur, la recherche de caractres ou de sous-chanes, etc. Les chanes de caractres en Java Une constante chane de caractres est une suite de caractres dnote entre guillemets. Tous les caractres du jeu U NICODE sont autoriss, y compris leur reprsentation spciale (voir page 30). Par exemple, la constante "bonjour\n tous \u2665" dnote les mots bonjour et tous, spars par un passage la ligne, et suivis du symbole n. Lenvironnement JAVA dnit deux classes, String et StringBuilder, pour crer et manipuler des chanes de caractres. Les objets de type String sont des chanes constantes, i.e., une fois cres, elles ne peuvent plus tre modies. Au contraire, les chanes de type StringBuilder peuvent tre modies dynamiquement. Ces deux classes offrent une multitude de services que nous ne dcrirons pas ici. Nous nous contenterons de prsenter quelques fonctions classiques de la classe String. La mthode length renvoie le nombre de caractres contenus dans la chane courante. La mthode charAt renvoie le caractre dont la position dans la chane courante est passe en paramtre. La position du premier caractre est zro. La mthode indexOf renvoie la premire position du caractre pass en paramtre dans la chane courante. La mthode compareTo compare la chane courante avec une chane passe en paramtre. La comparaison, selon lordre lexicographique, renvoie zro si les chanes sont identiques, une valeur entire ngative si lobjet courant est infrieur au paramtre, et une valeur entire positive si lobjet courant lui est suprieur. De plus, le langage dnit loprateur + qui concatne deux chanes3 , et dune faon gnrale deux objets munis de la mthode toString. Le fragment de code suivant dclare deux variables de type String et met en vidence les mthodes donnes plus haut :
3 Loprateur + possde ainsi plusieurs signications, puisque nous avons dj vu quil permettait dadditionner des valeurs numriques.

98

Chapitre 9 Les tableaux

String c1 = new String("bonjour "); // ou String c1 = "bonjour " ; String c2; c2 = c1 + " tous "; System.out.println(c2); System.out.println(c2.length()); System.out.println(c2.charAt(3)); System.out.println(c2.indexOf( o )); System.out.println(c1.compareTo(" tous "));

Lexcution de ce fragment de code produit les rsultats suivants :


bonjour 14 // j // 1 // -126 // tous longueur de la chane "bonjour tous" le quatrime caractre position du premier o "bonjour "<" tous"

Le rsultat de la comparaison parat surprenant, puisque la lettre a, mme accentue, prcde la lettre b dans lalphabet franais. En fait, elle suit lordre des caractres dans le jeu U NICODE. Toutefois, JAVA dnit la classe Collator (du paquetage java.text) qui connat les rgles de comparaisons spciques diffrentes langues internationales. la cration dune instance de type Collator, on indique le langage dsir, puis on utilise la mthode compare laquelle on passe en paramtre les deux chanes comparer. Le rsultat de la comparaison suit la mme convention que celle de compareTo. La comparaison suivante renvoie une valeur positive.
Collator fr = Collator.getInstance(Locale.FRENCH); System.out.println(fr.compare(c1, " tous "));

9.8

EXERCICES

Exercice 9.1. crivez et dmontrez la validit dune fonction qui calcule la moyenne des lments dun tableau dentiers. Exercice 9.2. crivez et dmontrez la validit dune fonction qui recherche la valeur minimale et la valeur maximale dun tableau de n entiers. Exercice 9.3. On appelle palindrome un mot ou une phrase qui, lu de gauche droite ou, inversement, de droite gauche garde le mme sens. Le mot radar ou la phrase esope reste et se repose sont des palindromes. crivez une fonction qui teste si une chane de caractres est un palindrome. Exercice 9.4. On dsire rechercher tous les nombres premiers compris entre 2 et une certaine valeur maximale n, selon lalgorithme du crible d RATOSTHNE4 . Le crible est la structure qui contient la suite dentiers ; il est reprsent habituellement par un tableau.
4 Mathmaticien et philosophe, connu pour ses travaux en arithmtique et en gomtrie, RATOSTHNE vcut au IIIe sicle avant J.C. Alexandrie.

9.8

Exercices

99

crivez et dmontrez la validit dune procdure qui afche sur la sortie standard les nombres premiers compris entre 2 et n selon lalgorithme :
Algorithme Crible d R A T O S T H N E initialiser le crible vide faux rpter {le plus petit nombre contenu dans le crible est premier} - afficher ce nombre sur la sortie standard - lenlever du crible avec tous ses multiples si le crible est vide alors vide vrai finsi jusqu vide

Notez que les lments du tableau peuvent tre simplement des boolens, puisque seule la prsence ou labsence du nombre dans le crible est signicative. Exercice 9.5. On cherche dnir une classe pour reprsenter des vecteurs de la forme v = [x1 , x2 , x3 , . . . , xn ]. Les composantes relles de chaque vecteur seront reprsentes par un tableau de rels. La dimension du vecteur, cest--dire le nombre dlments du tableau, est xe par le constructeur de la classe. crivez en JAVA une classe Vecteur avec un constructeur dont le paramtre est la dimension du vecteur. La dimension sera une caractristique de chaque objet de type Vecteur cr. crivez et dmontrez la validit de la mthode norme qui renvoie la norme dun vecteur. Puis, ajoutez une mthode normalise pour normaliser les composantes du vecteur courant. On rappelle que la norme dun vecteur v est gale :
n

v =
i=1

x2 i

et que la normalisation dun vecteur est donne par : v= v = v x1 x2 xn , ,..., v v v

crivez et dmontrez la validit de la mthode produitScalaire qui renvoie le produit scalaire du vecteur transmis en paramtre et du vecteur courant. On vriera que les deux vecteurs possdent bien la mme dimension. On rappelle que le produit scalaire de deux vecteurs v et v est gal :
n

v.v =
i=1

xi xi

Chapitre 10

Lnonc itratif pour

10.1

FORME GNRALE

Il arrive que lon ait besoin de faire le mme traitement sur toutes les valeurs dun type donn. Par exemple, on dsire afcher tous les caractres contenus dans le type caractre du langage de programmation avec lequel on travaille. Beaucoup de langages de programmation proposent une construction adapte ce besoin spcique, appele nonc itratif pour. Une forme gnrale de cette construction est un nonc qui possde la syntaxe suivante :
pourtout x de T faire E finpour

o x est une variable de boucle qui prendra successivement toutes les valeurs du type T. Pour chacune dentre elles, lnonc E sera excut. Notez, dune part, que lordre de parcours des lments du type T na pas ncessairement dimportance, et dautre part, que la variable de boucle nest dnie et nexiste, quau moment de lexcution de lnonc itratif. Lnonc suivant crit sur la sortie standard tous les caractres du type caractre.
pourtout c de caractre faire crire(c) finpour

Il est important de comprendre que le nombre ditrations ne dpend pas dun prdicat dachvement, contrairement aux noncs tantque ou rpter. Il est gal au cardinal du type T. On a la garantie que la boucle sachve et la nitude de la boucle nest donc plus dmontrer ! Dans un algorithme, chaque fois que vous aurez effectuer des itrations dont le nombre peut tre connu lavance de faon statique, vous utiliserez lnonc pourtout.

102

Chapitre 10 Lnonc itratif pour

10.2

FORME RESTREINTE

La plupart des langages de programmation dnissent des formes restreintes de lnonc gnral pourtout. En particulier, elles limitent le type T aux types lmentaires discrets, et xent un ordre de parcours sur un intervalle [min, max]. Cet ordre peut tre croissant ou dcroissant selon que la borne minimale est un lment de valeur infrieure ou suprieure celle de la borne maximale. Nous dnoterons cette forme restreinte comme suit :
pourtout x de min max faire E finpour

Avec cet nonc, lalgorithme dinitialisation dun tableau, donn la page 95, scrit simplement :
Algorithme Initialisation alatoire {Rle: initialise le tableau tab de faon alatoire} pourtout i de 1 n faire {k [1,i-1], tab[k] est initialis alatoirement} {random renvoie un nombre de faon alatoire} tab[i] random() finpour {i [1,n], tab[i] est initialis alatoirement}

Rgles de dduction La rgle de dduction de lnonc pour restreint dans le cas dun parcours croissant des valeurs de lintervalle est donn ci-dessous. La fonction pred renvoie le prdcesseur dun lment dans lintervalle ]min, max].
si {v = min et P } {Qvmin } et {Qpred(vx ) } {Qvx } x ]min, max] alors {min max et P } pourtout v de min max faire E finpour {Qvmax }
Ev Evx

De la mme manire, la rgle de dduction pour un parcours dcroissant de lintervalle est le suivant. La fonction succ renvoie le successeur dun lment dans lintervalle [min, max[.
si {v = max et P } {Qvmax } et {Qsucc(vx ) } {Qvx } x [min, max[ alors {min max et P } pourtout v de max min faire E finpour {Qvmin }
Ev Evx

10.3

LES NONCS POUR DE JAVA

JAVA propose deux formes dnonc pour. La smantique de la premire forme, similaire celle du langage C, na malheureusement pas le caractre fondamental des noncs algorithmiques prcdents, puisque le prdicat dachvement apparat dans sa notation :
for (exp1; exp2; exp3) E

o exp1 est une expression de dclaration et dinitialisation de la variable de boucle, exp2 est le prdicat dachvement, et exp3 est lexpression dincrmentation de la variable de boucle. Cet nonc est strictement quivalent lnonc tantque suivant :

10.4

Exemples

103

exp1; while (exp2) { E; exp3; }

Cette forme dnonc ne dispensera donc pas le programmeur de dmontrer la nitude de la boucle. Le constructeur de la classe TabAla de la page 96 scrit avec cet nonc :
// Rle : crer les n composants du tableau tab // et les initialiser de faon alatoire TabAla(int n) { // crer un gnrateur de nombres alatoires Random rand = new Random(); // crer les n composants du tableau tab=new int [n]; for (int i=0; i<n; i++) // k [0,i-1], tab[k] est initialis alatoirement tab[i]=rand.nextInt(); // i [0,n-1], tab[i] est initialis alatoirement }

Depuis sa version 5.0, le langage propose une forme gnralise de lnonc pour, appel foreach, lorsquil sagit dappliquer un mme traitement tous les lments dun tableau ou dune collection1 . Cet nonc possde la syntaxe suivante :
for (T x : TC) E

Il exprime que la variable x prendra successivement les valeurs (de type T) de tous les lments du tableau ou de la collection TC, et que pour chacune de ces valeurs lnonc E sera excut. On crira, par exemple, la mthode toString de notre classe TabAla comme suit :
// Rle : renvoie la reprsentation sous forme de chane de // caractres de lobjet courant de type TabAla public String toString() { String s = ""; for (int x : tab) s+=x + " "; return s; }

Notez que sil est ncessaire de faire une modication slective dun lment de tableau dans le corps de la boucle, cet nonc foreach ne peut tre utilis.

10.4
10.4.1

EXEMPLES
Le schma de H ORNER

Nous voulons calculer la valeur dun polynme p de degr n une variable reprsent par le tableau de ses coefcients :
1 Voir

java.util.Collection.

104

Chapitre 10 Lnonc itratif pour

variable cf type tableau [ [0,n] ] de rels

Une valeur de p pour une variable x est donne par : p(x) = cf[0] x0 + cf[1] x1 + cf[2] x2 + + cf[n] xn Les lvations la puissance successives rendent le calcul de p(x) trs coteux. Pour viter les calculs successifs des xi , on utilise le schma de H ORNER2 , donn ci-dessous : p(x) = (((. . . (cf[n] x + cf[n-1]) x + + cf[1]) x + cf[0] Par exemple, le polynme 3x3 2x2 + 4x + 1 est rcrit sous la forme ((3x 2)x + 4)x + 1. Linvariant de lalgorithme du calcul de la valeur p(x), par le schma de H ORNER, i valeur = k=n cf [k] xki , se dmontre aisment par application de la rgle de dduction donne plus haut.
Algorithme H O R N E R {Rle : calcul de la valeur dun polynme de degr n une variable x et le tableau des coefficients cf} valeur cf[n] Pn kn {valeur = = cf[n]} k=n cf[k]x pourtout i de n-1 0 faire Pi ki {valeur = } k=n cf[k]x valeur valeur x + cf[i] finpour P0 k {valeur = k=n cf[k]x } rendre valeur

Programmation en J AVA La programmation en JAVA de lalgorithme prcdent est donne ci-dessous. On considre que le tableau cf est un attribut de la classe dans laquelle la fonction Horner a t dnie.
public double Horner(double x) { // Rle : calcul de la valeur dun polynme // de degr n une variable int n=cf.length-1; double valeur=cf[n]; Pn kn // valeur = = cf[n] k=n cf[k]x for (int i=n-1; i>=0; i--) Pi ki // valeur = k=n cf[k]x valeur = valeur*x+cf[i]; P0 k // valeur = k=n cf[k]x return valeur; }
2 Redcouverte au dbut du XIXe sicle par langlais W. H ORNER , cette mthode est due au mathmaticien chinois C HU S HIH-C HIEH, 500 ans plus tt.

10.4

Exemples

105

10.4.2

Un tri interne simple

Une primitive de tri consiste ordonner, de faon croissante ou dcroissante, une liste dlments. Par exemple, si nous considrons la liste de valeurs entires suivante :
53 914 827 302 631 785 230 11 567 350

une opration de tri ordonnera ces valeurs de faon croissante et retournera la liste :
11 53 230 302 350 567 631 785 827 914

Nous prsentons une mthode de tri simple, appele tri par slection ordinaire. Ce tri est dit interne car lensemble des lments trier rside en mmoire principale, dans un tableau. Il existe de nombreuses mthodes de tri, plus ou moins performantes, que nous tudierons en dtail au chapitre 22 page 299. Le principe de la slection ordinaire est de rechercher le minimum de la liste, de le placer en tte de liste et de recommencer sur le reste de la liste. En utilisant la liste dentiers prcdente, le droulement de cette mthode donne les tapes suivantes. chaque tape, le minimum trouv est soulign.
| 53 914 827 302 631 785 230 11 567 11 | 914 827 302 631 785 230 53 567 11 53 | 827 302 631 785 230 914 567 11 53 230 | 302 631 785 827 914 567 11 53 230 302 | 631 785 827 914 567 11 53 230 302 350 | 785 827 914 567 11 53 230 302 350 567 | 827 914 785 11 53 230 302 350 567 631 | 914 785 11 53 230 302 350 567 631 785 | 914 11 53 230 302 350 567 631 785 827 | 350 350 350 350 350 631 631 827 827 914

Lalgorithme de tri suit un processus itratif dont linvariant spcie, dune part, qu la ie tape la sous-liste forme des lments de t[1] t[i-1] est trie, et dautre part, que tous ses lments sont infrieurs ou gaux aux lments t[i] t[n]. On en dduit que le nombre dtapes est n-1.
Algorithme Tri par slection ordinaire {Rle : Trie par slection ordinaire en ordre croissant} { les n valeurs dun tableau t} pourtout i de 1 n-1 faire {Invariant : le sous-tableau de t[1] t[i-1] est tri et ses lments sont infrieurs ou gaux aux lments t[i] t[n]} min i {chercher lindice du minimum sur lintervalle [i,n]} pourtout j de i+1 n faire si t[j] < t[min] alors min j finsi finpour {changer t[i] et t[min]} t[i] t[min] finpour {le tableau de t[1] t[n] est tri}

106

Chapitre 10 Lnonc itratif pour

Programmation en J AVA La procdure suivante programme lalgorithme de tri par slection ordinaire. Remarquez les dclarations des variables min et aux dans le corps de la boucle for la plus externe.
public void slectionOrdinaire(int [] t) { // Rle : Trie par slection ordinaire en ordre croissant // les valeurs du tableau t for (int i=0; i<t.length-1; i++) { // Invariant : le sous-tableau de t[0] t[i-1] est tri // et ses lments sont infrieurs ou gaux // aux lments t[i] t[t.length-1] int min=i; // chercher lindice du minimum sur lintervalle [i,t.length] for (int j=i; j<t.length; j++) if (t[j]<t[min]) min=j; // changer t[i] et t[min] int aux=t[i]; t[i]=t[min]; t[min]=aux; } // le tableau de t[0] t[t.length-1] est tri }

10.4.3

Confrontation de modle

La confrontation de modle (en anglais pattern matching ) est un problme classique de manipulation de chanes de caractres. Il sagit de rechercher la position pos dune occurrence dun mot (le modle) de longueur lgmot dans un texte de longueur lgtexte. Le texte et le mot sont forms de caractres pris dans un alphabet . Lide gnrale, est de comparer rptitivement le mot une portion du texte, appele fentre, possdant le mme nombre de caractres que le mot recherch. Une occurrence est trouve lorsque la fentre et le mot sont identiques. La premire mthode qui vient immdiatement lesprit est de comparer le mot toutes les fentres possibles du texte, en commenant par le premier caractre du texte, puis le second, etc. jusqu trouver une concordance entre le mot et une fentre. Cet algorithme est donn ci-dessous. Notez quil ne dpend pas de lalphabet . La fonction gal renvoie la valeur vrai si le mot est gal la fentre de position pos dans le texte.
Algorithme naf pos 1 tantque pos < lgtexte-lgmot faire si gal(mot, texte, pos) alors {le mot est la position pos dans le texte} rendre pos sinon {dplacer la fentre dune position} pos pos+1 finsi fintantque {le mot na pas t trouv dans le texte}

10.4

Exemples

107

Cette mthode nest pas trs efcace car elle teste toutes les positions possibles du mot dans le texte. Une seconde mthode beaucoup plus efcace, propose par B OYER et M OORE, exploite deux ides3 : 1. On peut comparer un mot une fentre en commenant par les caractres situs leurs extrmits. 2. Aprs un chec, au lieu davancer dun caractre, il est possible de faire un saut plus important. Si le dernier caractre test dans la fentre nest pas prsent dans le mot, on peut dplacer la fentre immdiatement aprs ce caractre. Sinon, on fait le plus court dplacement qui aligne le dernier caractre test de la fentre avec un caractre identique du mot. Si, par exemple, nous recherchons le mot noir dans le texte anoraks noirs, les diffrentes comparaisons et les dplacements d produits lors des checs sont donns ci-dessous :
a n o . . . i o r a . . . . . . r n o i k s n . . . . . . . . . r n o i r s

o n

r i

chec d = 1 chec d = 4 chec d = 3 succs

Aprs le premier chec, lalignement des deux lettres o provoque un dplacement dun caractre. Aprs le deuxime chec, et puisquil ny a pas de a dans le mot, la fentre est place immdiatement aprs cette lettre. La comparaison entre le n et le r choue. La lettre n est prsente dans le mot, lajustement produit un dplacement de trois caractres. Enn, le mot et la fentre sont identiques. Lalgorithme donn ci-dessous renvoie lindice dans le texte du premier caractre du mot recherch sil est trouv, sinon il renvoie la valeur 1. Nous reprsentons le mot et le texte par deux tableaux de caractres. La variable pos indique la position courante dans le texte. Nous traiterons le calcul de la valeur du dplacement plus loin.
Algorithme Boyer-Moore variables pos, i, j de type naturel variable diffrent de type boolen
pos 1 tantque pos lgtexte-lgmot+1 faire diffrent faux { On compare partir de la fin du mot et de la fentre} i lgmot j pos+lgmot-1 rpter si mot[i]=texte[j] alors
3 Il

en existe une troisime qui tient compte de sufxes dj reconnus dans le mot, mais qui ne sera pas voque

ici.

108

Chapitre 10 Lnonc itratif pour

{galit on poursuit la comparaison} i i-1 j j-1 sinon {diffrence on sarrte} diffrent vrai finsi jusqu i=0 ou diffrent si i=0 alors {la comparaison a russi, renvoyer la position} rendre pos sinon {chec: dplacer la fentre vers la droite} pos pos + {valeur du dplacement} finsi fintantque {le mot na pas t trouv dans le texte} rendre -1

Lorsque la comparaison entre le mot et la fentre courante a chou, cest--dire lorsque


mot[i]=texte[j], il faut dplacer la fentre vers la droite. Une faon de procder est de chercher un caractre mot[k] gal au caractre texte[j], et de produire le saut qui les

place lun en face de lautre. La gure 10.1 montre les trois cas qui peuvent se prsenter : (1) ce caractre nest pas prsent dans le mot ; (2) il apparat dans le mot avant texte[j] ; (3) il apparat dans le mot aprs texte[j].
pos texte mot chec texte mot pos chec texte mot pos chec

x x y x .... x x x x pos

x x y .... x y x x pos

x x x y ... x x x y pos

texte mot

x x y x .... x x x x (1)

texte mot

x x y .... x y x x (2)

texte mot

x x x y ... x x x y (3)

F IG . 10.1 Dplacements aprs chec de la comparaison.

Il est important de noter que le calcul du dplacement ne dpend que du mot, mais ncessite la connaissance de lalphabet . On pose ts(c) lindice du caractre c le plus droite dans mot ; si c mot alors ts(c) = 0. La valeur du dplacement produire est alors gale i ts(texte[j]). Notez que cette valeur peut tre infrieure ou gale zro (troisime cas). Plutt que de provoquer un retour en arrire, on dcale la fentre vers la droite dun caractre. Programmation en J AVA Nous reprsenterons les variables mot et texte par deux chanes de caractres de type String. La fonction ts est simplement reprsente par un tableau ts qui possde un nombre de composants gal au cardinal de lalphabet utilis. Nous considrerons les 256 premiers caractres du jeu U NICODE. Notez que le tableau est initialis 1 pour les caractres qui

10.5

Complexit des algorithmes

109

nappartiennent pas au mot. Rappelez-vous que lindice du premier lment dun tableau en JAVA est gal 0. La programmation de lalgorithme de B OYERM OORE est la suivante :
int BoyerMoore(String texte, String mot) { // Rle : recherche la premire occurrence du mot dans le texte // et renvoie sa position dans le texte ou -1 si non trouve int [] ts = new int[MAX_CAR]; // initialisation de la table ts for (int i=0; i<MAX_CAR; i++) ts[i]=-1; for (int i=0; i<mot.length(); i++) ts[mot.charAt(i)]=i; // rechercher loccurrence du mot int pos=0; while (pos <= texte.length()-mot.length()) { // on compare partir de la fin du mot et la fentre int i=mot.length()-1; int j=pos+i; while (i>=0 && mot.charAt(i)==texte.charAt(j)) { // galit => on poursuit la comparaison i--; j--; } if (i<0) // la comparaison a russi, renvoyer pos return pos; else // chec : dplacer la fentre vers la droite pos+=Math.max(1,i-ts[texte.charAt(j)]); } return -1; }

10.5

COMPLEXIT DES ALGORITHMES

Nous venons de mentionner quil existe de nombreuses mthodes de tri plus ou moins performantes, ou encore que la mthode de confrontation de modle de B OYERM OORE est plus efcace que celle qui consiste comparer le mot toutes les fentres possibles. Mais quoi correspond cette notion de performance ou defcacit et surtout comment lanalyser ? Lorsquon value les performances dun programme, on sintresse principalement son temps dexcution et la place en mmoire quil requiert. Dire, par exemple, que tel programme trie 1 000 lments en 0, 1 seconde sur telle machine et utilise quatre mga-octets en mmoire centrale na toutefois que peu de signication. Les caractristiques des ordinateurs, mais aussi des langages de programmation et des compilateurs qui les implantent, sont trop diffrentes pour tirer des conclusions sur les performances dun programme partir de mesures absolues obtenues dans un environnement particulier. Mais surtout, cela ne nous permet pas de prvoir le temps dexcution et lencombrement mmoire de ce mme programme pour le tri de 100 000 lments. Va-t-il rclamer 100 fois plus de temps et de mmoire ? Lestimation du temps dexcution ou de lencombrement en mmoire dun programme est fondamentale. Si lon peut prdire quen augmentant ses donnes dun facteur 100, un programme sexcutera en trois mois ou ncessitera dix giga-octets de mmoire, il sera certainement inutile de chercher lexcuter.

110

Chapitre 10 Lnonc itratif pour

Plutt que de donner une mesure absolue des performances dun programme, nous donnerons une mesure thorique de son algorithme, appele complexit, indpendante dun environnement matriel et logiciel particulier. Cette mesure sera fonction dlments caractristiques de lalgorithme et on supposera que chaque opration de lalgorithme prend un temps unitaire. Cette mesure ne nous permet donc pas de prvoir un temps dexcution exact, mais ce qui nous intresse vraiment cest lordre de grandeur de lvolution du temps dexcution (ou de lencombrement mmoire) en fonction dlments caractristiques de lalgorithme. Par exemple, dans le cas des tris, la mesure correspond au nombre de comparaisons dlments (parfois, on considre aussi le nombre de transferts) exprim en fonction du nombre dlments trier. ltape i du tri par slection de n lments (algorithme donn la page 105), la comparaison est excute par la boucle la plus interne n i fois. Puisquil y a n 1 n1 1 tapes, la comparaison est donc excute i=1 i = 2 (n2 n). La complexit temporelle 2 du tri par slection est dite quadratique ou O(n ). Cela signie que si on multiple par 100 le nombre dlments trier, on peut alors prdire que le temps dexcution du tri sera multipli par 1002 = 10 000. La complexit spatiale (encombrement mmoire) du tri par slection est linaire O(n). Si on multiple par 100 le nombre dlments trier, le programme utilisera un tableau 100 fois plus grand. Quelle est la signication de la notation O(n2 ) utilise plus haut et pourquoi ne considret-on que le terme n2 alors que le nombre exact de comparaisons est 1 (n2 n) ? 2 Soient deux fonctions positives f et g, on dit que f (n) est O(g(n)) sil existe deux constantes positives c et n0 telles que f (n) cg(n) pour tout n n0 . Lide de cette dnition est dtablir un ordre de comparaison relatif entre les fonctions f et g. Elle indique quil existe une valeur n0 partir de laquelle f (n) est toujours infrieur ou gal cg(n). On dit alors que f (n) est de lordre de g(n). La gure 10.2 donne une illustration graphique de cette dnition.
temps dexcution ou encombrement mmoire

cg(n)

f(n)

n0

lment caractristique de lalgorithme

F IG . 10.2 Reprsentation graphique de f (n) = O(g(n)).

Montrons que la fonction 1 (n2 n) est de lordre de O(n2 ). La dnition de la notation O 2 1 nous invite rechercher deux valeurs positives c et n0 telles que 2 (n2 n) cn2 . En prenant

10.6

Exercices

111

par exemple c = 1 , il est vident que nimporte quel n0 > 0 vrie lingalit. Remarquez 2 1 que nous aurions pu tout aussi bien crire que 2 (n2 n) est O(n3 ) ou O(n5 ), mais O(n2 ) est plus prcis. Dune faon gnrale, dans la notation f (n) = O(g(n)), on choisira une fonction g la plus proche possible de f , en respectant les rgles suivantes : cf (n) = O(f (n)) pour tout facteur constant c. f (n) + c = O(f (n)) pour tout facteur constant c. f (n) + g(n) = O(max(f (n), g(n))). f (n) g(n) = O(f (n) g(n)). si f (n) est un polynme de degr m, f (n) = a0 + a1 n + a2 n2 + . . . + am nm , alors f (n) est de lordre du degr le plus grand, i.e. O(nm ). nm = O(cn ) pour tout m > 0 et c > 1. log nm = O(log n) pour tout m > 0. log n = O(n) En appliquant ces rgles, on en dduit facilement que n6 + 3n3 5 est de lordre de O(n6 ), ou encore que 3n3 + n log n est de lordre de O(n3 ). Le tableau suivant prsente des fonctions, et les termes employs pour les dsigner, qui interviendront souvent dans lanalyse de la complexit des algorithmes que nous tudierons par la suite. 1 log2 n n n2 n3 cn (c > 1) constante logarithmique linaire quadratique cubique exponentielle

Lintrt de la notation O est dtre un vritable outil de comparaison des algorithmes. Par exemple, si, pour effectuer un tri, nous devons choisir entre le tri par slection dont la complexit, nous venons de le voir, est O(n2 ) et le tri en tas (voir la section 22.2.2, page 302) de complexit O(n log2 n), notre choix se portera sur le premier parce que n est petit et que le tri par slection est simple mettre en uvre, ou alors sur le second parce que le nombre dlments trier est tel quil rend le premier algorithme inutilisable.

10.6

EXERCICES

On dsire effectuer des calculs sur des entiers naturels trop grands pour tre reprsents laide du type prdni entier. On dnit pour cela un type nouveau GrandEntierNat. Un grand entier naturel est divis en chiffres, exprims dans une base b. Les chiffres sont mmoriss dans un tableau, de telle faon que la valeur dun nombre a de n chiffres est gale :
n1

a=
i=0

chiffres[i] bi

Exercice 10.1. Dnissez la classe GrandEntierNat avec les attributs ncessaires la reprsentation dun grand entier.

112

Chapitre 10 Lnonc itratif pour

Exercice 10.2. crivez quatre constructeurs qui initialisent un grand entier, respectivement, zro, la valeur dun entier pass en paramtre, la valeur entire dnie par une chane de caractres passe en paramtre, la valeur dun GrandEntierNat pass en paramtre. Exercice 10.3. crivez les quatre oprations lmentaires, addition, soustraction, multiplication et division. Exercice 10.4. crivez les fonctions de comparaison qui testent si deux grands entiers sont infrieurs, infrieurs ou gaux, suprieurs, suprieurs ou gaux, gaux ou diffrents. Exercice 10.5. crivez une fonction factorielle et calculez 32! Exercice 10.6. Calculez la somme la base e des logarithmes npriens.
k

1/k! pour k variant de 1 32. Cette somme est gale

Exercice 10.7. Donnez la notation O des fonctions n3 log n + 5, 2n5/2 et 2n . Exercice 10.8. Comment qualiez-vous la fonction n log2 n ? linaire ou logarithmique ? Exercice 10.9. Montrez que 2n+2 est O(2n ) et que (n + 2)4 est O(n4 ). Exercice 10.10. Quelles sont les complexits temporelles et spatiales de lalgorithme de calcul de la valeur dun polynme selon le schma de H ORNER ? Exercice 10.11. Recherchez la k e plus grande valeur dune suite quelconque dentiers lus sur lentre standard. Proposez deux algorithmes en O(n2 ). Notez quil existe une mthode dont la complexit est O(n log2 n) (voir le chapitre 21). Exercice 10.12. Dans lanalyse dun algorithme, on est bien souvent amen distinguer trois complexits. La complexit la meilleure, lorsque les donnes sont telles que lalgorithme a les meilleures performances, la complexit la pire, dans le cas dfavorable o les donnes conduisent aux performances les moins bonnes, et enn la complexit moyenne dans le cas gnral. Donnez les trois complexits temporelles de lalgorithme de B OYERM OORE. Exercice 10.13. La mthode des rectangles permet dapproximer lintgrale dune fonction f continue. Cette mthode consiste dcouper en n intervalles [ai , ai+1 ], de longueur identique m, lintervalle [a, b] sur lequel on veut intgrer la fonction f , puis additionner laire des rectangles de hauteur f ((ai + ai+1 )/2) et de largeur m. Laire A, approximation de lintgrale de f sur lintervalle [a, b], vaut donc :
n

A=
i=1

m f ((ai + ai+1 )/2)

La gure suivante illustre la mthode des rectangles dans le cas o n = 3.

10.6

Exercices

113

f(x)

a m

b = a + 3m

Programmez en JAVA une mthode qui calcule lintgrale de la fonction cosinus sur un intervalle [a, b]. Testez votre mthode, par exemple, sur lintervalle [0, /2] ; quel est le nombre dintervalles ncessaires pour obtenir un rsultat exact la cinquime dcimale ? Exercice 10.14. La mthode de S IMPSON fournit une approximation du calcul de lintgrale bien meilleure que la mthode des rectangles. Elle consiste calculer laire :
n

A=
i=1

m/6 (f (ai ) + 4 f (ai + m/2) + f (ai+1 ))

Programmez la mthode de S IMPSON et comparez-la de faon exprimentale avec la mthode des rectangles.

Chapitre 11

Les tableaux plusieurs dimensions

Les composants dun tableau peuvent tre de type quelconque et en particulier de type tableau. Les tableaux de tableaux sont souvent appels tableaux plusieurs dimensions. Certains langages de programmation, comme F ORTRAN ou A LGOL 68, ont une vision diffrente de cette notion, mais la plupart des langages de programmation actuels se conforment ce modle1 . En gnral, le nombre de dimensions nest pas limit, mais dans la pratique les programmes utilisent le plus souvent des tableaux deux dimensions et beaucoup plus rarement trois dimensions. Un tableau deux dimensions permet de reprsenter la notion mathmatique de matrice.

11.1

DCLARATION

La dclaration dun tableau deux dimensions possde la forme suivante :


variable t type tableau [T1 , T2 ] de T3

Bien sr, t est un tableau deux dimensions condition que T3 ne soit pas le type dun tableau. La dclaration dune matrice de rels qui possde m lignes et n colonnes est donne par :
1 Le langage PASCAL a t le premier la n des annes 60 proposer le modle de tableaux de tableaux pour reprsenter les tableaux plusieurs dimensions.

116

Chapitre 11 Les tableaux plusieurs dimensions

constantes m = 10 n = 20 variable matrice type tableau [ [1,m] , [1,n] ] de rel

Dune faon gnrale, on pourra dclarer un tableau n dimensions de la faon suivante :


t type tableau [T1 , T2 , . . . , Tn ] de Tc

o Tc est le type des composants du tableau n dimensions.


constante n = 10 variable table type tableau [caractre, boolen, [1,n]] de rel

Dans la dclaration prcdente, les types des indices de la premire, de la deuxime et de la troisime composante sont, respectivement, caractre, boolen et intervalle. Le type des composants de table est le type rel.

11.2

DNOTATION DUN COMPOSANT DE TABLEAU

Comme pour un tableau une dimension, les composants sont dnots au moyen du nom de la variable dsignant lobjet de type tableau et dindices qui dsignent de faon unique le composant dsir. Lordre des indices est celui dni par la dclaration.
matrice[1] {composant de type tableau [[1,n]] de rel} matrice[1,4] {composant de type rel} table[ f ] {composant de type tableau [boolen, [1,n]] de rel} table[ f ,vrai] {composant de type tableau [[1,n]] de rel} table[ f ,vrai,3] {composant de type rel}

Les indices sont des expressions dont les rsultats des valuations doivent appartenir au type de lindice associ.

11.3

MODIFICATION SLECTIVE

Les remarques faites sur la modication slective dun composant dun tableau une dimension (voir la section 9.3 page 93) sappliquent de faon identique un composant de tableau plusieurs dimensions. Le fait quun composant de tableau soit lui-mme de type tableau nintroduit aucune rgle particulire.

11.4

Oprations

117

11.4

OPRATIONS

Les oprations sur les tableaux plusieurs dimensions sont identiques celles sur les tableaux une dimension (voir la section 9.4 page 93).

11.5

TABLEAUX PLUSIEURS DIMENSIONS EN JAVA

Les tableaux plusieurs dimensions sont traits comme des tableaux de tableaux. Le nombre de dimensions peut tre quelconque et les rgles pour leur dclaration et leur cration sont semblables celles donnes dans la section 9.5 page 93. On dclare un tableau t n dimensions dont les composants sont de type Tc de la faon suivante :
Tc [][][] . . . [] t;

La cration des composantes du tableau t est explicite laide de loprateur new. Pour chacune des dimensions, on indique son nombre de composants :
t = new Tc [N1 ][N2 ] . . . [Nn ];

La dclaration de la matrice de rels m lignes et n colonnes de la section 11.1 scrit en JAVA comme suit :
double [][] matrice = new double [m][n];

Laccs aux lments de la matrice se fait par une double indexation, dnote
matrice[i][j], o i et j sont deux indices dnis, respectivement, sur les intervalles [0,m-1] et [0,n-1].

Notez que le nom de la variable qui dsigne le tableau (e.g. matrice), ou les composants dun tableau plusieurs dimensions (e.g. matrice[1]) sont des rfrences sur des tableaux une dimension. Le modle nest donc pas tout fait celui de tableaux de tableaux. En revanche, cela autorise un nombre de composants diffrents par dimension. Il nest pas obligatoire de crer toutes les composantes en une seule fois. Il est ainsi possible dcrire :
double [][] matrice = new double [m][];

La variable matrice dsigne un tableau de m composants initialiss null. Par la suite, chacun des composants pourra tre cr individuellement.
matrice[0] = new double[5];

...
matrice[3] = new double[10];

La premire ligne de la matrice possde 5 colonnes, alors que la quatrime en possde 10. Linitialisation dun tableau plusieurs dimensions peut se faire au moment de sa dclaration :
// matrice 2 3 int [][] matrice = { { 1, 2, 3 }, { 4, 5, 6 }};

118

Chapitre 11 Les tableaux plusieurs dimensions

11.6
11.6.1

EXEMPLES
Initialisation dune matrice

Soit la dclaration de la matrice m lignes et n colonnes suivante :


variable matrice type tableau [[1,m],[1,n]] de rel

On dsire initialiser tous les lments de la matrice une valeur relle v. Pour cela, il est ncessaire de parcourir toutes les lignes, et pour chaque ligne toutes les colonnes, laide de deux noncs itratifs pourtout embots.
Algorithme init matrice {initialisation la valeur v de tous les lments de la matrice} pourtout i de 1 m faire pourtout j de 1 n faire {x [1,i-1], y [1,j-1], matrice[x,y]=v} matrice[i,j] v finpour finpour {x [1,m], y [1,n], matrice[x,y]=v}

Le fragment de code JAVA correspondant lalgorithme prcdent est :


// initialisation la valeur v de tous les lments de la matrice for (int i = 0; i<m; i++) for (int j = 0; j<n; j++) // x [0,i-1], y [0,j-1], matrice[x,y]=v matrice[i][j]=v; // x [0,m-1], y [0,n-1], matrice[x,y]=v

11.6.2

Matrice symtrique

On dsire tester si une matrice carre est symtrique ou non par rapport la diagonale principale. On rappelle quune matrice carre m(n, n) est symtrique si i, j [1, n], mij = mji Lalgorithme consiste parcourir, laide de deux boucles embotes, la demi-matrice suprieure (ou infrieure) et vrier que m[i, j] = m[j, i]. Notez quil est inutile de tester les lments de la diagonale (i.e. i = j). Dautre part, le nombre ditrations ntant pas connu lavance, lnonc pourtout nest pas adapt au parcours des lignes et des colonnes. La complexit de cet algorithme est O(n2 ).
Algorithme symtrique {teste si une matrice est symtrique ou non} variables l, c type [1,n] passymtrique type boolen l 1 passymtrique faux

11.6

Exemples

119

rpter l l+1 c 1 rpter si matrice[l,c] = matrice[c,l] alors passymtrique vrai sinon c c+1 finsi jusqu c = l ou passymtrique {passymtrique ou i,j [1,l], matrice[i,j]=matrice[j,i]} jusqu l = n ou passymtrique {passymtrique ou i,j [1,n], matrice[i,j]=matrice[j,i]} rendre non passymtrique

On donne en JAVA la programmation de la fonction symtrique qui teste une matrice passe en paramtre. Notez la suppression de la variable boolenne passymtrique par lutilisation de linstruction return.
// teste si une matrice est symtrique ou non public boolean symtrique(int [][] matrice) { int l = 0; do { l++; int c = 0; do { if (matrice[l][c] != matrice[c][l]) // la matrice nest pas symtrique return false; c++; } while (c < l); // i,j [0,l], matrice[i,j]=matrice[j,i] } while (l < matrice.length-1); // i,j [0, matrice.length-1], matrice[i,j]=matrice[j,i] // la matrice est symtrique return true; }

11.6.3

Produit de matrices

Soient trois matrices a(m, p), b(p, n) et c(m, n), on dsire programmer le produit matriciel c = a b. Rappelons que les lments de la matrice c sont tels que
p

i [1, m], j [1, n], cij =


k=1

aik bkj

Lalgorithme suit prcisment cette quation, qui sera linvariant de boucle. Il possde trois noncs pourtout embotes et sa complexit est O(n3 ).

120

Chapitre 11 Les tableaux plusieurs dimensions

constantes m = ? {nombre lignes de la matrice a} n = ? {nombre lignes de la matrice b et nombre colonnes de la matrice a} p = ? {nombre colonnes de la matrice c} variables a type tableau [[1,m],[1,p]] de rel b type tableau [[1,p],[1,n]] de rel c type tableau [[1,m],[1,n]] de rel somme type rel pourtout i de 1 m faire pourtout j de 1 n faire Pp {x [1,i-1], y [1,j-1], c[x,y]= k=1 a[x,k]b[k,y]} somme 0 pourtout k de 1 p faire somme somme+a[i,k]b[k,j] finpour c[i,j] somme finpour finpour Pp {i [1,m], j [1,n], c[i,j]= k=1 a[i,k]b[k,j]}

Notez quil existe une autre mthode, celle de S TRASSEN, base sur la dcomposition de la matrice en quatre quadrants, de complexit infrieure O(n3 ).

11.6.4

Carr magique

On appelle carr magique2 une matrice carre dordre n, contenant les entiers compris entre 1 et n2 , telle que les sommes des entiers de chaque ligne, de chaque colonne et des deux diagonales sont identiques. La matrice suivante est un carr magique dordre 3 : 4 3 8 9 5 1 2 7 6

Nous prsentons une mthode de complexit O(n2 ) pour crer des carrs magiques dordre impair. Le chiffre 1 est mis dans la case situe une ligne en dessous de la case centrale. Lorsquon a plac un entier x dans une case de coordonnes (i, j), on place x + 1 dans la case (l, k) = (i + 1, j + 1). Cependant, ces coordonnes ne sont pas ncessairement valides. Si un indice est gal la valeur n + 1, on lui affecte la valeur 1 ; et si la case est dj occupe, alors on place le nombre en (l + 1, k 1), mais si k 1 = 0 alors k prend la valeur n. Il remarquable que si le premier calcul de coordonnes correspond une case
2 Les carrs magiques sont trs anciens, puisquon trouve leur trace, il y a prs de 3 000 ans, sur la carapace dune tortue dans la lgende chinoise de L O S HU. En Europe, le premier carr magique apparat en 1514 sur une gravure du peintre allemand A. D RER. Si durant de nombreux sicles ces carrs taient attachs des superstitions divines, partir du XVIIe sicle, ils ont fait lobjet de nombreuses tudes mathmatiques.

11.6

Exemples

121

dj occupe, le calcul suivant conduit toujours une place libre. crivons formellement lalgorithme selon cette mthode. Une case libre a pour valeur 0.
Algorithme Carr Magique {on considre le carr initialis 0} {crire le premier nombre dans la premire case} j (n+1)/2 i j+1 carr[i,j] 1 {placer les nombres suivants de 2 n2 } pourtout k de 2 n2 {calculer les prochaines coordonnes (i+1,j+1)} i i+1 si i>n alors i 1 finsi j j+1 si j>n alors j 1 finsi {est-ce que la place carr[i,j] est occupe ?} {si oui, trouver une place libre} si carr[i,j]=0 alors i i+1 si i>n alors i 1 finsi j j-1 si j=0 alors j n finsi finsi {carr[i,j] est la place de lentier k} carr[i,j] k finpour

La programmation en JAVA de cet algorithme consistera dnir une classe


CarrMagique avec comme attribut une matrice dentiers, et un constructeur qui cre le carr magique. Cette classe, complte par la mthode toString est entirement donne

ci-dessous :
public class CarrMagique { private int [][] carr; public CarrMagique(int n) { // Rle : cre un carr magique dordre n carr = new int[n][n]; int j=n/2, i=j+1; // on place le premier nombre dans la premire case carr[i][j]=1; // puis les suivants de 2 n2 final int nAuCarr=n*n; for (int k=2; k<=nAuCarr; k++) { // est-ce que les nouvelles coordonnes i et j // sont infrieures n ? sinon elles passent 0 if (++i==n) i=0; if (++j==n) j=0; // est-ce que la place est dj occupe ?

122

Chapitre 11 Les tableaux plusieurs dimensions

// si oui, trouver une place libre if (carr[i][j]!=0) { if (++i==n) i=0; if (--j<0) j=n-1; } assert carr[i][j]==0; // carr[i][j] est la place de lentier k carr[i][j]=k; } } public String toString() { String s=""; for (int [] ligne : carr) { for (int x : ligne) s += x + " "; s+= \ n ; } return s; } } // fin classe CarrMagique

11.7

EXERCICES

Exercice 11.1. Donnez en fonction de lordre n dun carr magique la valeur de la somme des cases dune ligne (ou colonne ou diagonale). Exercice 11.2. On dnit en JAVA la classe Matrice possdant trois attributs : un tableau deux dimensions qui contiendra les lments de la matrice, son nombre de lignes et de colonnes :
class Matrice { // Invariant : this est une matrice (lignes,colonnes) private double [][] m; public int nbLignes, nbColonnes; } // fin classe Matrice

Remarquez que la reprsentation de la matrice est prive, et inconnue des utilisateurs de cette classe. Quel est lintrt de masquer aux clients la reprsentation des lments de la matrice ? Si demain, la reprsentation change, le code du client ne changera pas et restera valide. la place dun tableau deux dimensions, on pourrait imaginer une autre reprsentation des lments de la matrice, en particulier si elle est creuse3 . Si nous voulons prserver cette indpendance vis--vis dune reprsentation particulire des donnes, nous devons dnir des mthodes qui la maintiennent. crivez les mthodes prendre et mettre. La premire renvoie la valeur de llment (i, j), la seconde lui affecte une valeur donne.
3 Une

matrice creuse est une matrice dont la majorit des lments est gale zro.

11.7

Exercices

123

Exercice 11.3. En utilisant les mthodes prcdentes, crivez les mthodes symtrique et produit dont les algorithmes sont dcrits plus haut. La premire mthode teste la symtrie de la matrice courante, et la seconde renvoie une matrice (i.e. de type Matrice) produit de la matrice courante et dune seconde passe en paramtre. Pour que le produit de deux matrices soit valide, vous vrierez si le nombre de colonnes de la premire est gal au nombre de lignes de la seconde. Exercice 11.4. crivez une mthode qui renvoie le vecteur rsultat du produit de la matrice courante par un vecteur pass en paramtre. On rappelle que le produit dune matrice a de dimension m n et du vecteur v de dimension n est le vecteur v de dimension m dont les composantes sont dnies par :
n

vi =
k=1

aik vk

Exercice 11.5. Soit le systme linaire de n quations n inconnues suivant : a11 x1 a21 x1 . . . an1 x1 a12 x2 a22 x2 . . . an2 x2 ... ... ... a1n xn = b1 a2n xn = b2 . . . ann xn = bn

On reprsente ce systme par lquation AX = B, o A est la matrice des coefcients aij et B est le vecteur des coefcients bi . a11 a21 . . . an1 a12 a22 an2 ... ... .. . ... a1n a2n . . . ann x1 x2 . . . xn b1 b2 . . . bn

La rsolution de ce systme linaire par la mthode de G AUSS4 se fait en deux tapes. La premire transforme la matrice du systme en une matrice triangulaire avec seulement des uns sur sa diagonale. La seconde calcule par substitution les solutions du systme, partir de la matrice triangularise, en partant de la dernire quation jusqu la premire. Ainsi, aprs triangularisation, le systme est transform en un systme quivalent : 1 a12 0 1 . . . 0 0 0 ... ... .. . ... a1n a2n . . . 1 x1 x2 . . . xn b1 b2 . . . bn

Pour triangulariser la matrice A, on procde de faon itrative du rang 1 au rang n. la k e tape, la sous-matrice (k 1, k 1) est triangularise et les oprations effectuer pour poursuivre la triangularisation sont les suivantes :
4 C.

F. G AUSS, astronome, mathmaticien et physicien allemand (1777-1855).

124

Chapitre 11 Les tableaux plusieurs dimensions

choisir le pivot ; normaliser la ligne, cest--dire diviser la ligne k du systme par le pivot (i.e. les akj et bk ) ; pour toute ligne i du systme allant de k + 1 n, lui soustraire la ligne de rang k multiplie par aik . Notez que ces oprations ne modient videmment pas la solution du systme dquations. Celle-ci se calcule par substitution en remontant partir de la dernire quation : xn = bn , xn1 = bn1 an1,n xn , etc. Lalgorithme de C. F. G AUSS a la forme suivante :
Algorithme GaussRSL(donnes A, B rsultat X) {Antcdent : A, B donnes du systme linaire} {Consquent : X solution du systme AX=B}
{triangularisation de la matrice A} pourtout k de 1 n faire pivot A[k,k] { A[k,k] est le pivot} {normaliser la ligne k de la matrice A et} {du vecteur B par le pivot tel que A[k,k]=1} ... pourtout i k+1 n faire {soustraire la ie ligne de la matrice A et} {du vecteur B la ligne k multiplie par A[i,k]} ... finpour finpour {calcul de la solution X} pourtout k de n 1 faire sol B[k] {calcul de la solution sol par substitution en remontant} {jusqu la k+1e ligne} ... X[k] sol finpour

Compltez lalgorithme prcdent, avec dans un premier temps, akk comme valeur de pivot. Que se passe-t-il si le pivot akk est nul ? Une solution est de chercher un pivot avk = 0 avec k + 1 v n, puis, dchanger la ligne v qui contient ce pivot avec la ligne k. Notez que si on ne peut trouver de pivot diffrent de zro, le systme est li et nadmet pas une solution unique. Dune faon gnrale, pour diminuer les risques derreurs dans les calculs numriques, on choisit le pivot le plus grand en valeur absolue entre les lignes k et n. Modiez lalgorithme en consquence. Quelle est la complexit de cet algorithme ? Exercice 11.6. On dsire calculer linverse dune matrice A. Pour cela, on procde de la mme manire que dans lexercice prcdent, mais B est la matrice identit. Pour inverser la matrice A, la mthode de G AUSS doit se poursuivre pour obtenir une double triangularisation (infrieure et suprieure) de A. Le systme initial est le suivant :

11.7

Exercices

125

a11 a21 . . . an1

a12 a22 a12

... ... .. . ...

a1n a2n . . . ann

1 0 . . . 0

0 ... 1 ... .. . 0 ...

0 0 . . . 1

La double triangularisation produit le systme suivant, o A est transforme en matrice identit, et B en la matrice inverse recherche : 1 0 . . . 0 0 ... 1 ... .. . 0 ... 0 0 . . . 1 b11 b21 . . . bn1 b12 b22 bn2 ... ... .. . ... b1n b2n . . . bnn

crivez lalgorithme dinversion dune matrice. Exercice 11.7. Appliquez la mthode de la double triangularisation pour rsoudre un systme dquations linaires. Comparez la complexit de cet algorithme avec celui de lexercice 11.5.

Chapitre 12

Hritage

Une des principales qualits constitutives des langages objets est la rutilisabilit, cest-dire la rutilisation de classes existantes. La rutilisation de composants logiciels dj programms et ables permet une conomie de temps et derreurs dans la construction de nouveaux programmes. Chaque anne, des millions de lignes de code sont crites et seul un faible pourcentage est original. Si cela se conoit dans un cadre pdagogique, a lest beaucoup moins dans un environnement industriel. Des algorithmes classiques sont reprogramms des milliers de fois, avec les risques derreur que cela comporte, au lieu de faire partie de bibliothques an dtre mis la disposition des programmeurs. La notion dhritage, que nous allons aborder dans ce chapitre, est loutil qui facilitera la conception des applications par rutilisation.

12.1

CLASSES HRITIRES

Dans le chapitre 7, nous avons dni une classe pour reprsenter et manipuler des rectangles. Si, maintenant, nous dsirons reprsenter des carrs, il nous faut dnir une nouvelle classe. Chaque carr est caractris par la longueur de son ct, son primtre, sa surface, etc. La classe pour reprsenter les carrs est dnie comme suit :
classe Carr {Invariant de classe : ct 0} public primtre, surface, ct {lattribut} ct type rel

128

Chapitre 12 Hritage

{le constructeur} constructeur Carr(donne c : rel) ct c fincons {les mthodes} fonction primtre() : rel {Rle: retourne le primtre du carr} rendre 4ct finfonc {primtre} fonction surface() : rel {Rle: retourne la surface du carr} rendre ctct finfonc {surface} finclasse Carr

Vous pouvez constater que cette classe ressemble fortement la classe Rectangle. Ceci est normal dans la mesure o un carr est un rectangle particulier dont la largeur est gale la longueur. Aussi, plutt que de dnir entirement une nouvelle classe, il est lgitime de rutiliser certaines des caractristiques dun rectangle pour dnir un carr. Lhritage est une relation entre deux classes qui permet une classe de rutiliser les caractristiques dune autre. Nous dnirons la classe Carr comme suit :
classe Carr hrite de Rectangle {Invariant de classe : longueur = largeur {le constructeur} constructeur Carr(donne c : rel) Rectangle(c,c) fincons finclasse Carr 0}

Tous les attributs et toutes les mthodes de la classe Rectangle, la classe parent, sont accessibles depuis la classe Carr, le descendant. On dit que la classe Carr hrite1 de la classe Rectangle. La classe Carr ne dnit que son constructeur, qui appelle celui de sa classe parent, et hrite automatiquement des attributs, largeur et longueur, ainsi que des mthodes primtre et surface. La gure 12.1 montre de faon graphique la relation dhritage entre les classes
Rectangle et Carr. Chaque classe est reprsente par une bote qui porte son nom et

lorientation de la che signie hrite de. La dclaration dun carr c et le calcul de sa surface sont alors obtenus par :
variable c type Carr crer Carr(5) ... c.surface() ...

La rutilisabilit des classes dj fabriques est un intrt majeur de lhritage. Il vite une perte de temps dans la rcriture de code dj existant, et vite ainsi lintroduction de nouvelles
1 On

dit galement drive et on parle alors de classe drive.

12.1

Classes hritires

129

Rectangle

Carr

F IG . 12.1 Relation dhritage entre les classes Rectangle et Carr.

erreurs dans les programmes. Lhritage peut tre vu comme un procd de factorisation, par la mise en commun des caractristiques communes des classes. Comme pour une gnalogie humaine, une classe hritire peut avoir ses propres descendants, crant ainsi un vritable arbre gnalogique. La relation dhritage est une relation transitive : si une classe B hrite dune classe A, et si une classe C hrite de la classe B, alors la classe C hrite galement de A. Dans beaucoup de langages de classe, toutes les classes possdent un anctre commun, une classe souvent appele Object, qui est la racine de larborescence dhritage (voir la gure 12.2).
Object

F IG . 12.2 Un anctre commun : la classe Object.

videmment, les classes hritires peuvent dnir leurs propres caractristiques. La classe
Carr possde dj son propre constructeur, mais pourra dnir, par exemple, une mthode

de mise jour du ct dun carr.

130

Chapitre 12 Hritage

classe Carr hrite de Rectangle {Invariant de classe : longueur = largeur public changerCt {le constructeur} constructeur Carr(donne c : rel) Rectangle(c,c) fincons procdure changerCt(donne c : rel) {Rle: met la valeur du ct la valeur c} changerLargeur(c) changerLongueur(c) finproc finclasse Carr ... c.changerCt(10)

0}

La relation dhritage peut donc aussi tre considre comme un mcanisme dextension de classes existantes, mais galement de spcialisation. Les informations les plus gnrales sont mises en commun dans des classes parentes. Les classes se spcialisent par lajout de nouvelles fonctionnalits. Il est important de comprendre que nimporte quelle classe ne peut tendre ou spcialiser nimporte quelle autre classe. La classe Carr qui hriterait dune classe Individu naurait aucun sens. Le mcanisme dhritage permet de conserver une cohrence entre les classes ainsi mises en relation.

12.2

REDFINITION DE MTHODES

Lorsquune classe hritire dsire modier limplmentation dune mthode dune classe parent, il lui suft de rednir cette mthode. La rednition dune mthode est ncessaire si on dsire adapter son action des besoins spciques. Imaginons, par exemple, que la classe Rectangle possde une mthode dafchage, la classe Carr peut rednir cette mthode pour ladapter ses besoins.
classe Rectangle ... procdure afficher() {Rle: affiche une description du rectangle courant} crire("rectangle de largeur", largeur, "et de longueur" , longueur) finproc ... finclasse Rectangle classe Carr hrite de Rectangle ... procdure afficher()

12.3

Recherche dun attribut ou dune mthode

131

{Rle: affiche une description du carr courant} crire("carr de ct gal " , largeur) finproc ... finclasse Carr

Dans le fragment de code suivant :


variable c type Carr crer Carr(5) variable r type Rectangle crer Rectangle(2,4) r.afficher() c.afficher()

il est clair que cest la mthode afficher de la classe Rectangle qui sapplique lobjet r et celle de la classe Carr qui sapplique lobjet c. Notez que les rednitions permettent de changer la mise en uvre des actions, tout en prservant leur smantique. Ainsi, la mthode afficher de la classe Carr ne devra pas calculer, par exemple, la surface dun carr.

12.3

RECHERCHE DUN ATTRIBUT OU DUNE MTHODE

Si la mthode que lon dsire appliquer une occurrence dun objet dune classe C nest pas prsente dans la classe, celle-ci devra appartenir lun de ses anctres. Dune faon gnrale, chaque fois que lon dsire accder un attribut ou une mthode dune occurrence dobjet dune classe C, il (ou elle) devra tre dni(e), soit dans la classe C, soit dans lun de ses anctres. Si lattribut ou la mthode nappartient pas aux classes parentes, lattribut ou la mthode nest pas trouv et cest une erreur de programmation. Sil y a eu des rednitions de la mthode, sa premire apparition en remontant larborescence dhritage est celle qui sera choisie. Ainsi, avec les dclarations suivantes :
variable c type Carr variable r type Rectangle

c.primtre(), provoque lexcution de la mthode primtre dnie dans la classe Rectangle, alors que r.changerCt() provoque une erreur.

12.4

POLYMORPHISME ET LIAISON DYNAMIQUE

Dans certains langages de programmation, une variable peut dsigner, tout moment au cours de lexcution dun programme, des valeurs de nimporte quel type. De tels langages sont dits non typs ou encore polymorphiques2 . En revanche, les langages dans lesquels une variable ne peut dsigner quun seul type de valeur sont dits typs ou monomorphiques. Ces derniers offrent plus de scurit dans la construction des programmes dans la mesure o les
2 Du

grec poly plusieurs et morphe forme.

132

Chapitre 12 Hritage

vrications de cohrence de type sont faites ds la compilation, alors quil faut attendre lexcution du programme pour les premiers. Dans un langage de classe typ, comme par exemple JAVA, le polymorphisme est contrl par lhritage. Ainsi, une variable de type Rectangle dsigne bien videmment des occurrences dobjets de type Rectangle, mais pourra galement dsigner des objets de type Carr. Si la variable r est de type Rectangle, et la variable c de type Carr, laffectation rc est valide. Cette affectation se justie puisquun carr est en fait un rectangle dont la largeur et la longueur sont gales. La relation dhritage qui lie les rectangles et les carrs est vue comme une relation est-un. Un carr est un rectangle (spcialis). En revanche, laffectation inverse, cr, nest pas licite, puisquun rectangle nest pas (ncessairement) un carr. Le polymorphisme des langages de classe typs permet dassouplir le systme de type, tout en conservant un contrle de type rigoureux. Imaginons que lon veuille manipuler des formes gomtriques diverses. Nous dnirons la classe Forme pour reprsenter des formes gomtriques quelconques. La classe Rectangle hritera de cette classe, puisquun rectangle est bien une forme gomtrique. De mme, nous dnirons des classes pour reprsenter des ellipses et des cercles. Larbre dhritage de ces classes est donn par la gure 12.3.
Forme

Ellipse

Rectangle

Cercle

Carr

F IG . 12.3 Arbre dhritage des gures gomtriques.

Les lments dun tableau de type Forme pourront dsigner, tout moment, des ellipses, des cercles, des rectangles ou encore des carrs. Sans le polymorphisme, il aurait fallu dclarer autant de tableaux quil existe de formes gomtriques.
t type tableau[ [1,max] ] de Forme t[1] crer Rectangle(3,10) t[2] crer Cercle(8) ... t[1] t[2] {t[1] dsigne (peut-tre) un cercle}

Chacune des classes, qui hrite de Forme, est pourvue dune mthode afficher qui produit un afchage spcique de la forme quelle dnit. Sil est clair quaprs la premire affectation :

12.5

Classes abstraites

133

t[1] crer Rectangle(3,10)

linstruction t[1].afficher() produit lafchage dun rectangle, il nen va pas de mme si cette mme instruction est excute aprs la dernire affectation :
t[1] t[2]

Pour cette dernire, la mthode appliquer ne peut tre connue qu lexcution du programme, au moment o lon connat la nature de lobjet qui a t affect t[1], cest--dire lobjet quil dsigne rellement. Ce sera la mthode afficher de la classe Cercle, si t[2] dsigne bien un cercle au moment de laffectation. Lorsquil y a des rednitions de mthodes, cest au moment de lexcution que lon connat la mthode appliquer. Elle est dtermine partir de la forme dynamique de lobjet sur lequel elle sapplique. Ce mcanisme, appel liaison dynamique, sapplique des mthodes qui possdent exactement les mmes signatures, proposant dans des classes diffrentes dune mme ascendance, la mise en uvre dune mme opration. Il a pour intrt majeur une utilisation des mthodes rednies indpendamment des objets qui les dnissent. On voit bien dans lexemple prcdent, quil est possible dafcher ou de calculer le primtre dune forme sans se soucier de sa nature exacte.

12.5

CLASSES ABSTRAITES

Dans la section prcdente, nous navons pas rdig le corps des mthodes de la classe
Forme. Puisque cette classe reprsente des formes quelconques, il est bien difcile dcrire les mthodes surface ou afficher. Toutefois, ces mthodes doivent tre ncessairement dnies dans cette classe pour quil y ait polymorphisme ; la variable t est un tableau de Forme et t[1].afficher() doit tre dnie. Il est toujours possible de dnir le corps des

mthodes vide, mais alors rien ne garantit que les mthodes seront effectivement rednies par les classes hritires. Une classe abstraite est une classe trs gnrale qui dcrit des proprits qui ne seront dnies que par des classes hritires, soit parce quelle ne sait pas comment le faire (e.g. la classe Forme), soit parce quelle dsire proposer diffrentes mises en uvres (voir les types abstraits, chapitre 16). Nous dnirons, par exemple, la classe Forme comme suit :
classe abstraite Forme public primtre, surface, afficher {les mthodes abstraites} fonction primtre() : rel fonction surface() : rel procdure afficher() finclasse abstraite Forme

Les mthodes dune telle classe sont appeles mthodes abstraites, et seuls les en-ttes sont spcis. Une classe abstraite ne peut tre instancie, il nest donc pas possible de crer des objets de type Forme. De plus, les classes hritires (e.g. Rectangle) sont dans lobligation de rednir les mthodes de la classe abstraite, sinon elles seront considres elles-mmes comme abstraites, et ne pourront donc pas tre instancies.

134

Chapitre 12 Hritage

12.6

HRITAGE SIMPLE ET MULTIPLE

Il arrive frquemment quune classe doive possder les caractristiques de plusieurs classes parentes distinctes. Une gure gomtrique forme dun carr avec en son centre un cercle dun rayon gal celui du ct du carr, pourrait tre dcrite par une classe qui hriterait la fois des proprits des carrs et des cercles (voir la gure 12.4). Cette classe serait par exemple contrainte par la relation largeur = longueur = diamtre.
Cercle Carr

CercledansCarr
F IG . 12.4 Graphe dhritage CercledansCarr.

Lorsquune classe ne possde quune seule classe parente, lhritage est simple. En revanche, si une classe peut hriter de plusieurs classes parentes diffrentes, lhritage est alors multiple. Avec lhritage multiple, les relations dhritage entre les classes ne dnissent plus une simple arborescence, mais de faon plus gnrale un graphe, appel graphe dhritage3 . Lhritage multiple introduit une complexit non ngligeable dans le choix de la mthode appliquer en cas de conit de noms ou dhritage rpt. Pour le programmeur, le choix dune mthode appliquer peut ne pas tre vident. Cest pour cela que certains langages de programmation, comme JAVA4 , ne le permettent pas.

12.7

HRITAGE ET ASSERTIONS

Le mcanisme dhritage introduit de nouvelles rgles pour la dnition des assertions des classes hritires et des mthodes quelles comportent.

12.7.1

Assertions sur les classes hritires

Linvariant dune classe hritire est la conjonction des invariants de ses classes parentes et de son propre invariant. Dans notre exemple, linvariant de la classe Carr est celui de la classe Rectangle, i.e. la largeur et la longueur dun rectangle doivent tre positives ou nulles, et de son propre invariant, i.e. ces deux longueurs doivent tre gales.
3 Voir

les chapitres 18 et 19 qui dcrivent les types abstraits graphe et arbre.

ce langage dnit la notion dinterface qui permet de mettre en uvre une forme particulire de lhritage multiple.

4 Toutefois,

12.8

Relation dhritage ou de clientle

135

12.7.2

Assertions sur les mthodes

Les rgles de dnition des antcdents et des consquents sur les mthodes doivent tre compltes dans le cas particulier de la rednition. Nous prendrons ici les rgles donnes par B. M EYER [Mey97]. Une assertion A est plus forte quune assertion B, si A implique B. Inversement, nous dirons que B est plus faible. Lors dune rednition dune mthode m, que nous appellerons m , il faudra que : (1) lantcdent de m soit plus faible ou gal que celui de m ; (2) le consquent de m soit plus fort ou gal que celui de m. Pour comprendre cette rgle, il faut la voir la lumire de la liaison dynamique. Une variable dclare de type classe A peut appeler la mthode m, mais excuter sa rednition m dans la classe hritire B sous leffet de la liaison dynamique. Cela indique que toute assertion qui sapplique m doit galement sappliquer m . Aussi, la rgle (1) indique que m doit accepter lantcdent de m, et la rgle (2) que m doit galement vrier le consquent de m.

12.8

RELATION DHRITAGE OU DE CLIENTLE

Lors de la construction dun programme, comment choisir les relations tablir entre les classes ? Une classe A doit-elle hriter dune classe B, ou en tre la cliente ? Une premire rponse est de dire que si on peut appliquer la relation est-un, sans doute faudra-t-il utiliser lhritage. Dans notre exemple, un carr est-un rectangle particulier dont la largeur et la longueur sont gales. La classe Carr hrite de la classe Rectangle. En revanche, si cest une relation a-un qui doit sappliquer, il faudra alors tablir une relation de clientle. Une voiture a-un volant, une cole a-des lves. La classe Voiture, qui reprsente des automobiles, possdera un attribut volant qui le dcrit. De mme, les lves dune cole peuvent tre reprsents par la classe lves, et la classe cole possdera un attribut pour dsigner tous les lves de lcole. Cette rgle possde lavantage dtre simple et nous lutiliserons chaque fois que cela est possible. Toutefois, elle ne pourra tre applique systmatiquement, et nous verrons par la suite des cas o elle devra tre mise en dfaut.

12.9

LHRITAGE EN JAVA

En JAVA, lhritage est simple et toutes les classes possdent implicitement un anctre commun, la classe Object. On retrouve dans ce langage les concepts exposs prcdemment, et dans cette section, nous nvoquerons que ses spcicits. Les classes hritires, ou sous-classes, comportent dans leur en-tte le mot-cl extends5 suivi du nom de la classe parente. Le constructeur dune sous-classe peut faire appel un
5 Ce

qui indique bien lide dextension de classe.

136

Chapitre 12 Hritage

constructeur de sa classe parente, la super-classe appele super. Sil ne le fait pas, un appel implicite au constructeur par dfaut de la classe parente, cest--dire super(), aura systmatiquement lieu. Notez que le constructeur par dfaut de la classe mre doit alors exister. La classe Carr scrit en JAVA :
public class Carr extends Rectangle { /** Invariant de classe : longueur = largeur 0 */ // le constructeur public Carr(double c) { // appel du constructeur de Rectangle super(c,c); } public void changerCt(double ct) // Rle : met jour le ct du carr courant { changerLargeur(ct); changerLongueur(ct); } public String toString() // Rle : convertit le carr courant en chane de caractres { return " c a r r de ct gal " + largeur; { } // fin classe Carr

La cration dun carr c de ct 7 et lafchage de sa surface scriront comme suit :


Carr c = new Carr(7); System.out.println(c.surface());

La rednition des mthodes dans les sous-classes ne peut se faire quavec des mthodes qui possdent exactement les mmes signatures. La liaison dynamique est donc mise en uvre sur des mthodes qui possdent les mmes en-ttes et qui diffrent par leurs instructions, comme par exemple la mthode toString des classes Rectangle et Carr. Les classes abstraites sont introduites par le mot-cl abstract, de mme que les mthodes. Notez que seule une partie des mthodes peut tre dclare abstraite, la classe demeurant toutefois abstraite. La classe Forme possde la dclaration suivante :
abstract class Forme { public abstract double primtre(); public abstract double surface(); }

Remarquez labsence de la mthode toString, puisque celle-ci est hrite de la classe


Object.

Contrairement aux classes abstraites, les interfaces sont des classes dont toutes les mthodes sont implicitement abstraites et qui ne peuvent possder dattributs, lexception de constantes. Les interfaces permettent, dune part, une forme simplie de lhritage multiple, et dautre part la gnricit. Nous reparlerons de ces deux notions plus loin, partir du chapitre 16.

12.9

Lhritage en Java

137

Linterface de programmation dapplication de JAVA (API6 ) est une hirarchie de classes qui offrent aux programmeurs des classes prfabriques pour manipuler les chiers, construire des interfaces graphiques, tablir des communications rseaux, etc. La classe Object est au sommet de cette hirarchie. Elle possde en particulier deux mthodes clone() et equals(Object o). La premire permet de dupliquer lobjet courant, et la seconde de comparer si lobjet pass en paramtre est gal lobjet courant. Ces mthodes sont ncessaires puisque les oprations daffectation et dgalit mettant en jeu deux oprandes de type classe manipulent des rfrences et non pas les objets eux-mmes. Il nest pas question de prsenter ces classes ici ; ce nest dailleurs pas lobjet de cet ouvrage. Toutefois, il faut mentionner que les types simples primitifs du langage possdent leur quivalent objet dont la correspondance est donne par la table 12.1. Type primitif
byte short int long float double boolean char

Classe correspondante
Byte Short Integer Long Float Double Boolean Character

TAB . 12.1 Correspondance types primitifs classes.

Notez que depuis sa version 5.0, le langage permet des conversions implicites entre un type primitif et son quivalent objet, et rciproquement. JAVA appelle ce mcanisme AutoBoxing. Par exemple, il est dsormais possible dcrire :
Character c = a ; int i = new Integer(10);

Rgles de visibilit Nous avons dj vu quun membre dune classe pouvait tre quali public, pour le rendre visible par nimporte quelle classe, et private, pour restreindre sa visibilit sa classe de dnition. Le langage JAVA propose une troisime qualication, protected, qui rend visible le membre par toutes les classes hritires de sa classe de dnition. En fait, les membres qualis protected sont visibles par les hritiers de la classe de dnition, mais galement par toutes les classes du mme paquetage (package). En JAVA, un paquetage est une collection de classes place dans des chiers regroups dans un mme rpertoire ou dossier, selon la terminologie du systme dexploitation utilis. Un paquetage regroupe des classes qui possdent des caractristiques communes, comme par exemple le paquetage java.io pour toutes les entres/sorties. La directive import suivie du nom dun paquetage permet de dnoter les noms que dnit ce dernier, sans les prxer par le nom du paquetage. Par exemple, les deux dclarations de variables suivantes sont quivalentes :
6 Applications

Programming Interface.

138

Chapitre 12 Hritage

import java.util.Random; Random x;

ou alors

java.util.Random x;

Depuis la version 5.0, JAVA spcialise la directive import pour laccs non quali aux objets dclars static dune classe. Ainsi, au lieu dcrire :
i1 = Math.sqrt(-)/(2*a);

on pourra crire :
import static java.lang.Math.*; ... i1 = sqrt(-)/(2*a);

Des rgles de visibilit sont galement dnies pour les classes. Une dclaration dune classe prxe par le mot-cl public rendra la classe visible par nimporte quelle classe depuis nimporte quel paquetage. Si ce mot-cl napparat pas, la visibilit de la classe est alors limite au paquetage. Les classes dont on veut limiter la visibilit au paquetage sont, en gnral, des classes auxiliaires ncessaires au bon fonctionnement des classes publiques du paquetage, mais inutiles en dehors.

Chapitre 13

Les exceptions

Lexcution anormale dune action peut provoquer une erreur de fonctionnement dun programme. Jusqu prsent, lorsquune situation anormale tait dtecte, les programmes que nous avons crits signalaient le problme par un message derreur et sarrtaient. Cette faon dagir est pour le moins brutale, et expditive. Dans certains cas, il serait souhaitable quils reprennent le contrle an de poursuivre leur excution. Les exceptions offrent une solution ce problme.

13.1

MISSION DUNE EXCEPTION

Une exception est un vnement qui indique une situation anormale, pouvant provoquer un dysfonctionnement du programme. Son origine est trs diverse. Il peut sagir dexceptions matrielles, par exemple lors dune lecture ou dune criture sur un quipement externe dfectueux, ou encore dune allocation mmoire impossible car lespace mmoire est insufsant. Ce type dexception nest pas directement de la responsabilit du programme (ou du programmeur). En revanche, les exceptions logicielles le sont, comme par exemple, une division par zro ou lindexation dun composant de tableau en dehors du domaine de dnition du type des indices. Plus gnralement, le non respect des spcications dun programme (antcdents, consquents, invariants de boucle ou de classe) provoque des exceptions logicielles. Lorsquune exception signale le mauvais droulement dune action, cette dernire arrte le cours normal de son excution. Lexception est alors prise en compte ou non. Si elle ne lest pas, le programme sarrte dnitivement. Bien souvent, ceci nest pas acceptable, et en principe, le comportement habituel est, si possible, de traiter lexception an de poursuivre un droulement normal du programme, cest--dire en respectant ses spcications.

140

Chapitre 13 Les exceptions

13.2

TRAITEMENT DUNE EXCEPTION

On distingue couramment deux faons de traiter une exception qui se produit au cours de lexcution dune action. La premire consiste excuter nouveau laction en changeant les conditions initiales de son excution. Il sagit donc de changer les donnes de laction, ou mme de changer son algorithme. Laction peut tre rexcute une ou plusieurs fois jusqu ce que son consquent nal soit atteint. La seconde mthode consiste transmettre lexception lenvironnement dexcution de laction. Sil le peut, ce dernier traitera lexception, ou alors la transmettra son propre environnement. Les environnements dexcution sont en gnral des contextes dappel de sous-programmes. La gure 13.1 montre une chane dappels de fonctions ou de procdures, symbolise par les ches pleines, depuis lenvironnement initial E1 , jusqu un environnement E4 dans lequel se produit une exception.
E1

E2

E3

E4

exception

F IG . 13.1 Une chane dappels.

Les ches en pointill indiquent les transmissions possibles de lexception aux environnements dappel. Notez que la chane des appels est parcourue en sens inverse. Chaque environnement peut traiter ou transmettre lenvironnement prcdent lexception. Si, en dernier ressort, lexception nest pas traite par lenvironnement initial E1 , le support dexcution se charge darrter le programme aprs avoir assur diverses tches de terminaison (fermetures de chiers, par exemple). La plupart des langages de programmation qui possdent la notion dexception proposent des mcanismes qui permettent de mettre en uvre ces deux mthodes de gestion dune exception. En revanche, notre connaissance, seul E IFFEL inclut le concept de rexcution (instruction retry). Dautre part, son modle dexception est troitement li avec celui de la programmation contractuelle du langage [Mey97]. De faon plus formelle, lobjectif du traitement dune exception est de maintenir la cohrence du programme. Dans le cas dune programmation par objets, le traitement de lexception devra maintenir linvariant de classe et le consquent de la mthode dans laquelle sest produite lexception. Si lexception est transmise lenvironnement dappel, seul le maintien de linvariant de classe est ncessaire.

13.3

Le mcanisme dexception de Java

141

Nous allons prsenter maintenant la faon dont les exceptions sont gres dans le langage JAVA.

13.3

LE MCANISME DEXCEPTION DE JAVA

Une exception est dcrite par un objet, occurrence dune sous-classe de la classe
Throwable. Cette classe possde deux sous-classes directes. La premire, la classe Error,

dcrit des erreurs systmes comme par exemple labsence de mmoire. Ces exceptions ne sont en gnral pas traites par les programmes. La seconde, la classe Exception, dcrit des exceptions logicielles traiter lorsquelles surviennent. Issues de ces deux classes, de nombreuses exceptions sont prdnies par lAPI. Un programmeur peut galement dnir ses propres exceptions par simple hritage. La classe Throwable possde deux constructeurs que la nouvelle classe peut rednir.
class MonException extends Exception { public MonException () { ... } public MonException (String s) { ... } }

13.3.1

Traitement dune exception

Pour traiter une exception produite par lexcution dune action A, il faut placer la mthode dans une clause try, suivie obligatoirement dune clause catch qui contient le traitement de lexception.
try {

A
} catch (UneException e) {

B
}

Si laction A contenue dans la clause try du fragment de code prcdent dtecte une anomalie qui met une exception de type UneException, son excution est interrompue. Le programme se poursuit par lexcution de laction B place dans la clause catch associe lexception UneException. Si aucune situation anormale na t dtecte, lexception UneException na donc pas t mise, laction A est excute intgralement et laction B ne lest pas du tout. Dans la clause catch, e dsigne lexception qui a t rcupre et qui peut tre manipule par B. La mthode suivante contrle la validit dune valeur entire lue sur lentre standard. Si la lecture produit lexception IOException (e.g. la valeur lue nest pas un entier), la clause catch capture lexception et propose une nouvelle lecture. Notez que le nombre de lectures possibles nest pas born.

142

Chapitre 13 Les exceptions

public int lireEntier() { try { return StdInput.readInt(); } catch (IOException e) { System.out.println(" r e s sa y e z : "); return lireEntier(); } }

Plusieurs clauses catch, associes des exceptions diffrentes, peuvent suivre une clause try. Chacune des clauses catch correspond la capture dune exception susceptible dtre mise par les noncs de la clause try. Si ncessaire, lordre des clauses catch doit respecter la hirarchie dhritage des exceptions. Une clause finally peut galement tre place aprs les clauses catch. Les noncs quelle contient seront toujours excuts quune exception ait t mise ou non, capture ou non. Cette clause possde la forme suivante :
finally { noncs }

Une mthode qui contient une action susceptible de produire des exceptions nest pas tenue de la placer dans une clause try suivie dune clause catch. Dans ce cas, elle doit indiquer dans son en-tte, prcde du mot-cl throws, les exceptions quelle ne dsire pas capturer. Si une exception apparat, alors lexcution se poursuit dans lenvironnement dappel de la mthode. Chaque mthode dune chane dappel peut traiter lexception ou la transmettre son environnement dappel.

13.3.2

mission dune exception

Lmission explicite dune exception est produite grce linstruction throw. Le type de lexception peut tre prdni dans lAPI, ou dni par le programmeur.
if (une situation anormale) throw new ArithmeticException(); if (une situation anormale) throw new MonException("un message");

Notez quune mthode (ou un constructeur) qui met explicitement une exception doit galement le signaler dans son en-tte avec le mot-cl throws, sauf si lexception drive de la classe RuntimeException.

13.4

EXERCICES

Exercice 13.1. Aprs chaque chec de lecture, la mthode lireEntier de la page 142 essaie une nouvelle lecture sans limiter le nombre de tentatives. Ceci peut tre une source

13.4

Exercices

143

majeure de problmes, si, par exemple, la saisie du nombre lire est faite automatiquement par un programme qui produit systmatiquement une valeur errone. Modiez la mthode lireEntier an de limiter le nombre de tentatives, et de transmettre lexception lenvironnement dappel si aucune des tentatives na russi. Exercice 13.2. crivez une mthode qui calcule linverse dun nombre rel x quelconque, i.e. 1/x. Lorsque x est trop petit, lopration produit une division par zro, mais la mthode devra renvoyer dans ce cas la valeur zro. Exercice 13.3. Modiez le constructeur de la classe Date donne page 77 an quil renvoie lexception DateException si le jour, le mois et lanne ne correspondent pas une date valide. Vous dnirez une classe DateException pour reprsenter cette exception.

Chapitre 14

Les chiers squentiels

Jusqu prsent, les objets que nous avons utiliss dans nos programmes, taient placs en mmoire centrale. lissue de lexcution du programme, ces objets disparaissaient. Cette rexion appelle deux questions : 1. que faire si on dsire manipuler des objets dune taille suprieure la mmoire centrale ? 2. que faire si on veut conserver ces donnes aprs la n de lexcution du programme ? Les chiers sont une rponse ces deux questions. Il est trs important de comprendre que ce concept de chier, propre un langage de programmation donn, trouve sa ralisation effective dans les mcanismes dentres-sorties avec le monde extrieur grce aux dispositifs priphriques de lordinateur. Les chiers permettent de conserver de linformation sur des supports externes, en particulier des disques. Mais dans bien des systmes, comme U NIX par exemple, les chiers ne se limitent pas cette fonction, ils reprsentent galement les mcanismes dentre et de sortie standard (i.e. le clavier et lcran de lordinateur), les priphriques, des moyens de communication entre processus ou rseau, etc. Il existe plusieurs modles de chier. Celui que nous tudierons, et que tous les langages de programmation mettent en uvre, est le modle squentiel. Les chiers squentiels jouent un rle essentiel dans tout systme dexploitation dordinateur. Ils constituent la structure approprie pour conserver des donnes sur des dispositifs priphriques, pour lesquels les mthodes de lecture ou dcriture des donnes passent dans un ordre strictement squentiel. Les chiers squentiels modlisent la notion de suite dlments. Quelle est la signication du qualicatif squentiel ? Il signie que lon ne peut accder un composant quaprs avoir accd tous ceux qui le prcdent. Lors du traitement dun chier squentiel, un moment donn, un seul composant du chier est accessible, celui qui correspond la position courante du chier. Les oprations dnies sur les chiers squentiels permettent de modier cette position courante pour accder au composant suivant.

146

Chapitre 14 Les chiers squentiels

14.1

DCLARATION DE TYPE

Un objet de type chier squentiel forme une suite de composants tous de mme type T, lmentaire ou structur. La dclaration est note :
fichier de T

Le type T des lments peut tre nimporte quel type lexception du type chier ou de tout type structur dont un composant serait de type chier. En dautres termes, les chiers de chiers ne sont pas autoriss. Le nombre de composants nest pas x par cette dclaration, cest--dire que le domaine des valeurs dun objet de type chier est (thoriquement) inni. Par exemple, les dclarations suivantes dnissent deux variables f1 et f2, respectivement, de type chier dentiers et de rectangles :
variables f1 type fichier de entier f2 type fichier de Rectangle

Les chiers squentiels se prtent deux types de manipulation : la lecture et lcriture. Nous considrerons par la suite que ces deux manipulations ne peuvent avoir lieu en mme temps ; un chier est soit en lecture, soit en criture, mais pas les deux la fois.

14.2

NOTATION

Par la suite, nous utiliserons les notations suivantes qui nous permettront dexprimer lantcdent et le consquent des oprations de base sur les chiers. Une variable f de type fichier de T est la concatnation de tous les lments qui prcdent la position courante, dsigns par f , et de tous les lments qui suivent la position courante, dsigns par f . Llment courant situ la position courante est not f

= f & f

f = premier( f )

Une suite de composants de chier est note entre les deux symboles < et >. Par exemple,
<5 -3 10> dnit une suite de 3 entiers, et <> la suite vide.

La dclaration de type chier nindique pas le nombre de composants. Ce nombre est quelconque. Nous verrons plus loin quil nous sera ncessaire, lors de la manipulation des chiers, de savoir si nous avons atteint la n du chier ou pas. Pour cela, nous dnissons la fonction fdf, pour n de chier, qui renvoie vrai si la n de chier est atteinte et faux sinon. La signature de cette fonction est : fdf : Fichier boolen Notez que :

fdf(f) f = <>

14.3

Manipulation des chiers

147

14.3

MANIPULATION DES FICHIERS

Les deux grandes formes de manipulation des chiers sont lcriture et la lecture. La premire est utilise pour la cration des chiers, la seconde pour leur consultation.

14.3.1

criture

Nous nous servirons des oprations dcriture chaque fois que nous aurons crer des chiers. Pour crer un chier, il est ncessaire deffectuer au pralable une initialisation grce la procdure Initcriture. Son effet sur un chier f est donn par :
{} Initcriture(f) {f = <> et fdf(f)}

Linitialisation en criture dun chier a donc pour effet de lui affecter une suite de composants vide et peu importe si le chier contenait des lments au pralable. La procdure crire ajoute un composant la n du chier. Remarquez que le prdicat fdf(f) est toujours vrai.
{f = x, e = tT et fdf(f)}} crire(f,e) {f = x & <t> et fdf(f)}

partir de ces deux oprations, nous pouvons donner le schma de la cration dun chier :
Algorithme cration dun fichier {initialisation} Initcriture(f) tantque B faire {calculer un nouveau composant} calculer(e) {lcrire la fin du fichier} crire(f,e) fintantque

Lexpression boolenne B est un prdicat qui contrle la n de cration du chier. Donnons, par exemple, lalgorithme de cration dun chier de n rels tirs au hasard avec la fonction random. laide du modle prcdent, nous crirons :
Algorithme cration dun fichier dentiers donne n type naturel rsultat f type fichier de rel {Antcdent : n 0} {Consquent : i=n et f contient n rels tirs au hasard} Initcriture(f) i 0 tantque i = n faire i i + 1 crire(f,random()) fintantque

148

Chapitre 14 Les chiers squentiels

14.3.2

Lecture

Une fois le chier cr, il peut tre utile de consulter ses composants. La consultation dun chier dbute par une initialisation en lecture grce la procdure InitLecture. Pour dcrire son fonctionnement, nous distinguerons le cas o le chier est initialement vide et le cas o il ne lest pas.
{f = <>} InitLecture(f)

{fdf(f) et f = f = <>} {f = x} InitLecture(f) {f = f , f = <> et non fdf(f) et f=premier(x)}


Notez que linitialisation en lecture dun chier non vide a pour effet daffecter la variable tampon la premire valeur du chier. Lopration de lecture, lire, renvoie la valeur de llment courant et change la position courante (i.e. passe la suivante). Nous allons distinguer le cas o llment lire est le dernier du chier et le cas o il ne lest pas.
{ f = x et f = <t> et non fdf(f) et f = t} e lire(f)

{ f = x & <t> et f = <> et fdf(f) et e = t} {f = x et f = <t> & y et non fdf(f) et f = t} e lire(f) { f = x & <t> et f = y et f = premier(y) et non fdf(f) et e=t}

Notez que toute tentative de lecture aprs la n de chier est bien souvent considre par les langages de programmation comme une erreur. Le schma gnral de consultation dun chier est donn par lalgorithme suivant :
Algorithme consultation dun fichier {initialisation} InitLecture(f) tantque non fdf(f) faire {f est llment courant du fichier lu} e lire(f) traiter(e) fintantque

Nous dsirons crire un algorithme qui calcule la moyenne des lments du chier de rels que nous avons cr plus haut.
Algorithme moyenne {Antcdent : f fichier de rels} {Consquent : moyenne = moyenne des rels contenus dans le fichier f ; si f est vide moy = 0}

14.4

Les chiers de Java

149

variables nblt type naturel moyenne type rel InitLecture(f) moyenne 0 nblt 0 tantque non fdf(f) faire {moyenne = fi et non fdf(f)} i=1 moyenne moyenne + lire(f) nblt nblt + 1 fintantque si nblt = 0 alors moyenne 0 sinon moyenne moyenne/nblt finsi rendre moyenne

Plongueur() f

14.4

LES FICHIERS DE JAVA

En JAVA, un ot (en anglais stream) est un support de communication de donnes entre une source mettrice et une destination rceptrice. Ces deux extrmits sont de toute nature ; ce sont par exemple des chiers, la mmoire centrale, ou encore un programme local ou distant. Les ots sont des objets dnis par deux familles de classes. La premire, reprsente par les classes InputStream et OuputStream, sont des ots doctets (8 bits) utiliss pour lchange de donnes de forme quelconque. La seconde, reprsente par les classes Reader et Writer, dnit des ots de caractres Unicode (cods sur 16 bits) qui servent en particulier la manipulation de texte. Le paquetage java.io contient toute la hirarchie des classes de ots qui assurent toutes sortes dentre-sortie. Leur description complte nest pas du ressort de cet ouvrage. Nous ne prsenterons dans cette section que les classes qui permettent la manipulation squentielle de chiers de donnes, lmentaires et structures. Nous traiterons le cas particulier des chiers de texte la n du chapitre.

14.4.1

Fichiers doctets

On utilise les chiers doctets pour manipuler de linformation non structure, ou du moins dont la structure est sans importance pour le traitement effectuer. La dclaration et louverture dun chier en lecture, respectivement en criture, est faite avec la cration dun objet de type FileInputStream, respectivement FileOutputStream :
FileInputStream is = new FileInputStream(" entre "); FileOutputStream os = new FileOutputStream(" s o r t i e ");

Ces classes offrent plusieurs constructeurs. Celui de lexemple prcdent admet comme donne une chane de caractres qui reprsente un nom de chier. Il est galement possible

150

Chapitre 14 Les chiers squentiels

de lui fournir un descripteur dun chier dj ouvert, ou un objet de type FILE (une reprsentation des noms des chiers indpendante du systme dexploitation). Parmi les mthodes proposes par les classes FileInputStream et FileOutputStream, il faut retenir plus particulirement les mthodes : read de FileInputStream qui lit le prochain octet du chier et le renvoie sous forme dun entier ; cette fonction renvoie lentier -1 lorsque la n du chier est atteinte ; write de FileOutputStream qui crit son paramtre (de type un byte) sur le chier ; close qui ferme le chier. La mthode suivante assure une copie de chier sans se proccuper de son contenu.
// copie du fichier source dans le fichier destination public void copie (String source, String destination) throws IOException { FileInputStream is = new FileInputStream(source); FileOutputStream os = new FileOutputStream(destination); int c; while ((c = is.read()) != -1) // os = is os.write((byte) c); // fin de fichier de is // fermer les fichiers is et os is.close(); os.close(); }

Notez que cette mthode signale la transmission possible dune exception IOException qui peut tre dclenche par les constructeurs en cas derreur douverture des chiers.

14.4.2

Fichiers dobjets lmentaires

Les classes DataInputStream ou DataOutputStream permettent de structurer, en lecture ou en criture, des chiers doctets en chiers de type lmentaire. Les constructeurs de ces deux classes attendent donc des objets de type FileInputStream ou FileOutputStream.
DataInputStream is = new DataInputStream(new FileInputStream(" entre ")); DataOutputStream os = new DataOutputStream(new FileOutputStream(" s o r t i e "));

Ces classes fournissent des mthodes spciques selon la nature des lments lire ou crire (entiers, rels, caractres, etc.). Le nom des mthodes est compos de read ou write sufx par le nom du type lmentaire (e.g. readInt, writeInt, readFloat, writeFloat, etc.). Les oprations de lecture mettent lexception EOFException lorsque la n de chier est atteinte. Le traitement de lexception place dans une clause catch consistera en gnral

14.4

Les chiers de Java

151

fermer le chier laide de la mthode close. On peut regretter lutilisation par le langage du mcanisme dexception pour traiter la n de chier. Est-ce vraiment une situation anormale que datteindre la n dun chier lors de son parcours ? Programmons les deux algorithmes de cration dun chier dentiers tirs au hasard, et du calcul de leur moyenne. Nous dnirons une classe Fichier qui contiendra un constructeur qui fabrique le chier dentiers, et une mthode moyenne qui calcule la moyenne. Cette classe possde un attribut priv, nomFich, qui est le nom du chier.
class Fichier { private String nomFich; Fichier(String nom, int n) throws IOException // Rle : cre un fichier de n entiers tirs au hasard { nomFich = nom; // crer un gnrateur de nombres alatoires Random rand = new Random(); DataOutputStream f = new DataOutputStream (new FileOutputStream(nomFich)); for (int i=0; i<n; i++) f.writeInt(rand.nextInt()); // le fichier f contient n rels tirs au hasard f.close(); } public double moyenne() throws IOException // Rle : renvoie la moyenne des valeurs du fichier courant { DataInputStream f = new DataInputStream(new FileInputStream(nomFich)); int nblt=0, moyenne=0; try { while (true) { // moyenne = fi et non fdf(f) i=1 moyenne += f.readInt(); nblt++; } } catch (EOFException e) { // fin de fichier de f f.close(); } return nblt==0 ? 0 : moyenne/(double) nblt; } } // fin classe Fichier

Plongueur() f

On dsire maintenant fusionner deux suites croissantes dentiers. Ces deux suites sont contenues dans deux chiers, f et g. Le rsultat de la fusion est une troisime suite ellemme croissante place dans le chier h.
f = -10 -2 0 5 89 100 g = -50 0 1 h = fusionner(f,g) = -50 -10

-2

89

100

152

Chapitre 14 Les chiers squentiels

Lalgorithme lit le premier lment de chacun des chiers f et g. Il parcourt ensuite les deux chiers simultanment jusqu atteindre la n de chier de lun ou de lautre. Les lments courants des deux chiers sont chaque fois compars. Le plus petit est crit sur le chier h. Sur le chier qui le contenait, on lit le prochain entier. Lorsque la n de lun des deux chiers est atteinte (remarquez quil nest pas possible datteindre la n des deux chiers simultanment), les entiers restants sont crits sur h, puisque suprieurs tous ceux qui prcdent. Cet algorithme sexprime formellement :
Algorithme fusionner donnes f, g type fichiers de entier rsultat h type fichier de entier {Antcdent : f et g deux fichiers qui contiennent des suites croissantes dentiers} {Consquent : h = suite croissante dentiers rsultat de la fusion des suites de f et g} InitLecture(f) InitLecture(g) Initcriture(h) si non fdf(f) et non fdf(g) alors {les deux fichiers ne sont pas vides} x lire(f) y lire(g) finsi tantque non fdf(f) et non fdf(g) faire {mettre dans h min(f,g) et passer au suivant} si x y alors crire(h,x) x lire(f) sinon {x>y} crire(h,y) y lire(g) finsi fintantque {fdf(f) xou fdf(g)} si fdf(f) alors {recopier tous les lments de g la fin de h} crire(h,y) recopier(g,h) sinon {recopier tous les lments de f la fin de h} crire(h,x) recopier(f,h) finsi

Lalgorithme de la procdure de recopie ne possde aucune difcult dcriture :


procdure recopier(donne f : fichier de entier rsultat g : fichier de entier {Antcdent : f non vide et ouvert en lecture g ouvert en criture} {Rle : recopie la fin de g les lments de f} variable x type entier

14.4

Les chiers de Java

153

rpter x lire(f) crire(g,x) jusqu fdf(f) finproc {recopier}

Programmons en JAVA cet algorithme avec la mthode fusionner qui compltera la classe Fichier. Cette mthode fusionne les deux chiers passs en paramtre. Lobjet courant contient le rsultat de la fusion.
public void fusionner(String f1, String f2) // Antcdent : f1 et f2 deux noms de fichiers qui contiennent des // suites croissantes dentiers // Consquent : le fichier courant nomFich est la suite croissante // dentiers rsultat de la fusion des suites de f1 et f2 throws IOException, EOFException { DataInputStream f = new DataInputStream(new FileInputStream(f1)); DataInputStream g = new DataInputStream(new FileInputStream(f2)); DataOutputStream h = new DataOutputStream(new FileOutputStream(nomFich)); int x, y; // lire le premier entier de chacun des fichiers try { x = f.readInt(); } catch (EOFException e) { // fdf(f) recopier g sur h recopier(g,h); return; } try { y = g.readInt(); } catch (EOFException e) { // fdf(g) recopier x et f sur h h.writeInt(x); recopier(f,h); return; } // les fichiers h et g contiennent tous les deux au moins un entier while (true) // mettre dans h min(f,g) et passer au suivant if (x<=y) { // crire x sur h h.writeInt(x); try { x = f.readInt(); } catch (EOFException e) { // fdf(f) recopier y et g sur h h.writeInt(y); recopier(g,h); return; } } else { // x>y crire y sur h h.writeInt(y);

154

Chapitre 14 Les chiers squentiels

try { y = g.readInt(); } catch (EOFException e) { // fdf(g) recopier x et f sur h h.writeInt(x); recopier(f,h); return; } } } } // fin fusionner

Vous remarquerez que la recopie des derniers entiers, lorsque la n dun des chiers est dtecte, est faite dans la boucle, et non pas aprs comme lalgorithme le suggre. Si une clause catch avait t place aprs lnonc itratif, elle aurait attrape lexception EOFException, mais sans pouvoir en dterminer la provenance (i.e. n de chier de f ou de g). La mthode recopie est dclare prive, dans la mesure o elle nest utilise que dans la classe.
private void recopier(DataInputStream i, DataOutputStream o) // Antcdent : fichier i non vide et ouvert en lecture // fichier o ouvert en criture // Rle : recopie la fin de o les lments de i { try { while (true) o.writeInt(i.readInt()); } catch (EOFException e) { // fdf(i) i.close(); o.close(); } }

14.4.3

Fichiers dobjets structurs

La lecture et lcriture de donnes structures sont faites en reliant des objets de type FileInputStream ou FileOutputStream avec des objets de type ObjectInputStream ou ObjectOutputStream. Ces classes fournissent respectivement les mthodes readObject et writeObject pour lire et crire un objet. Le fragment de code suivant crit dans le chier Frect un objet de type Rectangle (dni au chapitre 7), puis le relit.
Rectangle r = new Rectangle(3,4); ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(" F r e c t ")); // criture dun objet de type Rectangle sur le fichier os os.writeObject(r); os.close(); ObjectInputStream is = new ObjectInputStream(new FileInputStream(" F r e c t "));

14.5

Les chiers de texte

155

// lecture dun objet de type Rectangle sur le fichier is r = (Rectangle) is.readObject(); is.close();

Notez que la mthode readObject renvoie des objets de type Object qui doivent tre explicitement convertis si la rgle de compatibilit de type lexige. Dans notre exemple, la conversion de lobjet lu en Rectangle est impose par le type de la variable r1 . Si le type de conversion nest pas celui de lobjet lu, il se peut alors que lerreur ne soit dcouverte qu lexcution. Par exemple, si lobjet lu est de type Rectangle, le fragment de code suivant ne produit aucune erreur de compilation.
Integer z = (Integer) f.readObject(); System.out.println(z);

De plus, la mthode readObject met lexception EOFException si la n de chier est atteinte. Enn, les classes qui dnissent des objets qui peuvent tre crits et lus sur des chiers doivent spcier dans leur en-tte quelles implantent linterface Serializable. Len-tte de la classe Rectangle scrit alors :
public class Rectangle implements Serializable { ... }

Si elles ne le font pas, les mthodes writeObject et readObject mettront, respectivement, les exceptions NotSerializableException et ClassNotFoundException.

14.5

LES FICHIERS DE TEXTE

Les chiers de texte jouent un rle fondamental dans la communication entre le programme et les utilisateurs humains. Ces chiers ont des composants de type caractre et introduisent une structuration en ligne. Un chier de texte est donc vu comme une suite de lignes, chaque ligne tant une suite de caractres quelconques termine par un caractre de n de ligne. Certains langages comme le langage PASCAL dnissent mme un type spcique pour les reprsenter. Pour dautres langages, ils sont simplement des chiers de caractres. Alors que les lments de ces chiers de texte sont des caractres, bien souvent les langages de programmation autorisent lcriture et la lecture dlments de types diffrents, mais qui imposent une conversion implicite. Par exemple, il sera possible dcrire ou de lire un entier sur ces chiers. Lcriture de lentier 125 provoque sa conversion implicite en la suite de trois caractres 1, 2 et 5 successivement crits dans le chier de texte. Il est important de bien comprendre que les chiers de texte ne contiennent que des caractres, et que la lecture ou lcriture dobjet de type diffrent entrane une conversion de type depuis ou vers le type caractre. La plupart des langages de programmation dnit des chiers de texte lis implicitement au clavier et lcran de lordinateur. Ces chiers sont appels chier dentre standard et chier de sortie standard. Certains langages proposent un chier de sortie derreur standard dont les
1 Le

compilateur signale une erreur si la conversion nest pas explicitement faite.

156

Chapitre 14 Les chiers squentiels

programmes se servent pour crire leurs messages derreurs. Le chier dentre standard ne peut videmment tre utilis quen lecture, alors que ceux de sortie standard et de sortie derreur standard ne peuvent ltre quen criture. Ces chiers sont toujours automatiquement ouverts au dmarrage du programme.

14.6

LES FICHIERS DE TEXTE EN JAVA

Les ots de texte sont construits partir des sous-classes issues des classes Reader, pour les ots dentre, et Writer, pour ceux de sortie. Le type des lments est le type char (U NICODE). Les classes FileReader et FileWriter permettent de dnir les chiers de caractres, mais la structuration en ligne des chiers de texte nest pas explicitement dnie. Toutefois, le caractre \n permet de reprer la n dune ligne. crivons le programme qui compte le nombre de caractres, de mots et de lignes contenus dans un chier de texte2 . On considre les mots comme des suites de caractres spars par des espaces, des tabulations ou des passages la ligne. La seule petite difcult de ce problme vient de ce quil faut reconnatre les mots dans le texte. Il est rsolu simplement laide dun petit automate deux tats, dansMot et horsMot. Si ltat courant est dansMot et le caractre courant est un sparateur, ltat devient horsMot et on incrmente le compteur de mots puisquon vient dachever la reconnaissance dun mot. Si ltat courant est horsMot et le caractre courant nest pas un sparateur, ltat devient dansMot. Notez que lincrmentation du compteur de mot aurait pu tout aussi bien se faire au moment de la reconnaissance du premier caractre dun nouveau mot. Le tableau suivant rsume les changements dtats et les actions effectuer. c tat sparateur tathorsMot dansMot nbmotnbmot+1 tatdansMot non sparateur

horsMot

Algorithme wc variables f type fichier de texte tat type (dansMot, horsMot) c type caractre {le caractre courant}
tat horsMot tantque non fdf(f) faire {lire le prochain caractre de f} c lire(f) {incrmenter le compteur de caractres}
2

linstar de la commande wc du systme dexploitation U NIX.

14.6

Les chiers de texte en Java

157

nbcar nbcar+1 si c est un sparateur alors si tat = dansMot alors {fin dun mot incrmenter le compteur de mots} nbmots nbmots+1 tat horsMot finsi si c = fin de ligne alors {fin dune ligne incrmenter le compteur de lignes} nblignes nblignes+1 finsi sinon {c est un caractre dun mot} si tat = horsMot alors tat dansMot finsi finsi fintantque {fin de fichier afficher les rsultats} crire(nbLignes, nbMots, nbCar)

La programmation de cet algorithme est donne ci-dessous. Dans la mesure o ltat courant ne possde que deux valeurs, nous le reprsentons par une variable boolenne. Notez que la n dun chier de type Filereader est dtecte lorsque la valeur renvoye par la mthode read est gale -1, et que ceci impose que le type de la variable c soit le type entier int. Pour la traiter comme un caractre, il faut alors la convertir explicitement dans le type char.
import java.io.*; public class Wc { public static void main(String [] args) throws IOException { if (args.length != 1) { // le programme attend un seul nom de fichier System.err.println("Usage : j a v a Wc f i c h i e r "); System.exit(1); } FileReader is = null; try { is = new FileReader(args[0]); } catch (FileNotFoundException e) { System.err.println(" f i c h i e r " +args[0]+ " non trouv"); System.exit(2); } boolean tatDansMot=false; int c, nbCar=0, nbMots=0, nbLignes=0; while ((c=is.read()) != -1) { // incrmenter le compteur de caractres nbCar++; if (Character.isWhitepace((int) c)) { // c est un sparateur de mot

158

Chapitre 14 Les chiers squentiels

if (tatDansMot) { // fin dun mot incrmenter le compteur de mots nbMots++; tatDansMot=false; } if (c== \ n ) // fin dune ligne incrmenter le compteur de lignes nbLignes++; } else // on est dans un mot if (!tatDansMot) tatDansMot=true; } // fin de fichier afficher les rsultats System.out.println(nbLignes + " " + nbMots + " " + nbCar); } } // fin classe Wc

Vous avez remarqu lutilisation, pour la premire fois, du paramtre args de la mthode main. Ce tableau contient les paramtres de commande passs au moment de lexcution de lapplication. Wc attend un seul paramtre, le nom du chier sur lequel le comptage sera effectu, contenu dans la chane de caractres args[0]. Louverture en lecture du chier est place dans une clause try an de vrier quil est bien lisible. Les classes FileReader et FileWriter ne permettent de manipuler que des caractres. LAPI de JAVA propose la classe PrintWriter, connecter avec FileWriter, pour crire des objets de nimporte quel type, aprs une conversion implicite sous forme dune chane de caractres. Pour les objets non lmentaires, la conversion est assure par la mthode toString. Cette classe propose essentiellement deux mthodes dcriture, print et println. Cette dernire crit en plus le caractre de passage la ligne. Le fragment de code suivant recopie un chier source dans un chier destination en numrotant les lignes3 . La valeur entire du compteur de lignes est crite en dbut de ligne grce la mthode print.
FileReader is=new FileReader(source); PrintWriter os=new PrintWriter(new FileWriter(destination)); int c, nbligne=1; os.print(1 + " "); while ((c=is.read()) != -1) { os.write(c); if (c== \ n ) // dbut dune nouvelle ligne os.print(++nbligne + " "); } is.close(); os.close();

Il est tonnant que lAPI ne propose pas de classe quivalente PrintWriter pour lire des objets de type quelconque partir de leur reprsentation sous forme de caractres. Aucune
3 Voir

galement lexercice correspondant page 160.

14.7

Exercices

159

classe standard ne dnit, par exemple, une mthode readInt qui lit une suite de chiffres sur un chier de type FileReader pour renvoyer la valeur entire quelle reprsente. Pourtant une telle classe est trs utile, et bien souvent, les programmeurs sont amens construire leur propre classe. Le langage JAVA propose trois chiers standard : lentre standard System.in, la sortie standard System.out et la sortie derreur standard System.err. Les mthodes offertes par System.in sont celles de la classe InputStream, et ne permettent de lire que des caractres sur huit bits ; aucune noffre la possibilit de lire des objets de type quelconque aprs conversion. Les chiers System.out et System.err de type PrintStream utilisent galement des ots doctets, mais la mthode print (ou println) permet lcriture de nimporte quel type dobjet. Depuis la version 6, le paquetage java.io propose la classe Console qui permet de grer, sil existe, le dispositif dentre/sortie interactif li la machine virtuelle JAVA. Une instance (unique) de Console est renvoye par la mthode System.console(), ou null si elle nexiste pas. Les mthodes reader et writer renvoient, respectivement, des instances de type Reader et PrintWriter, les ots associs lentre et la sortie de la console. Les mthodes de lecture de la classe Console ne permettent pas la lecture dobjets de type lmentaire. Toutefois, ce type de lecture peut tre assur laide dun petit analyseur de texte propos par la classe Scanner du paquetage java.util. Dans cet ouvrage, nous utilisons la classe StdInput pour lire sur lentre standard des objets de type lmentaire. Ce nest pas une classe standard de lAPI. Elle a t dveloppe par lauteur pour des besoins pdagogiques et est disponible sur son site. Pour conclure, signalons lexistence des classes InputStreamReader et OutputStreamWriter qui sont des passerelles entre les ots doctets et les ots de caractres U NICODE. La plupart du temps, les systmes dexploitation codent les caractres des chiers de texte sur huit bits, et ces classes permettront la conversion dun octet en un caractre U NICODE cod sur seize bits, et rciproquement. Notez que les instructions suivantes :
new FileReader(f) new FileWriter(f)

sont strictement quivalentes :


new InputStreamReader(new FileInputStream(f)) new OutputStreamWriter(new FileOutputStream(f))

14.7

EXERCICES

Exercice 14.1. Reprogrammez lalgorithme d RATOSTHNE de la page 98 en choisissant un chier squentiel dentiers pour reprsenter le crible. Notez que vous aurez besoin dun second chier auxiliaire. Exercice 14.2. crivez un programme qui supprime tous les commentaires dun chier contenant des classes JAVA. On rappelle quil existe trois formes de commentaires.

160

Chapitre 14 Les chiers squentiels

Exercice 14.3. Le fragment de code (donn la page 158) qui recopie le contenu dun chier en numrotant les lignes a un lger dfaut. Il numrote systmatiquement une dernire ligne inexistante. Modiez le code an de corriger cette petite erreur. Exercice 14.4. crivez un programme qui lit lentre standard et qui afche sur la sortie standard le nombre de mots, le nombre dentiers et le nombre de caractres autres lus. Un mot est une suite de lettres alphabtiques, un entier est une suite de chiffres (0 9), et un caractre autre est ni une lettre alphabtique, ni un chiffre, ni un espace. Exercice 14.5. Rdigez une classe FichierTexte qui gre la notion de ligne des chiers de texte. Vous dnirez toutes les mthodes du modle algorithmique de chier donn dans ce chapitre, compltes par les fonctions fdln, lireln et crireln. La fonction fdln retourne un boolen qui indique si la n dune ligne est atteinte ou pas. La procdure lireln fait passer la ligne suivante, et la procdure crireln crit la marque de n de ligne. Avec ces nouvelles fonctions, lalgorithme de consultation dun chier de texte a la forme suivante :
{initialisation} InitLecture(f) tantque non fdf(f) faire {on est en dbut de ligne, traiter la ligne courante} tantque non fdln(f) faire c lire(f) traiter(c) {traiter le caractre courant} fintantque {on est en fin de ligne, passer la ligne suivante} lireln(f) fintantque

Exercice 14.6. Compltez la classe FichierTexte an de permettre des lectures dobjets de types lmentaires, et des critures dobjets de type quelconque. Exercice 14.7. Le jeu Le mot le plus long de la clbre mission tlvise Des chiffres et des lettres consiste obtenir, partir de neuf lettres (consonnes ou voyelles tires au hasard), un mot du dictionnaire le plus long possible. crivez un programme JAVA qui tire alatoirement neuf lettres et qui crit sur la sortie standard le mot plus long quil est possible dobtenir avec ce tirage. Vous pourrez placer le dictionnaire de mots sur un ou plusieurs chiers.

Chapitre 15

Rcursivit

Les fonctions rcursives jouent un rle trs important en informatique. En 1936, avant mme lavnement des premiers ordinateurs lectroniques, le mathmaticien A. C HURCH1 avait mis la thse2 que toute fonction calculable, cest--dire qui peut tre rsolue selon un algorithme sur une machine, peut tre dcrite par une fonction rcursive. Dans la vie courante, il est possible de dnir un oignon comme de la pelure doignon qui entoure un oignon. De mme, une matriochka est une poupe russe dans laquelle est contenue une matriochka. Ces deux dnitions sont dites rcursives. On parle de dnition rcursive lorsquun terme est dcrit partir de lui-mme. En mathmatique, certaines fonctions, comme par exemple la fonction factorielle n! = n(n1)!, sont galement dnies selon ce modle. On parle alors de dnition par rcurrence. Les prcdentes dnitions de loignon et de la matriochka sont innies, en ce sens quon ne voit pas comment elles sachvent, un peu comme quand on se place entre deux miroirs qui se font face, et quon aperoit son image reproduite linni. Pour tre calculables, les dnitions rcursives ont besoin dune condition darrt. Un oignon possde toujours un noyau quon ne peut pas peler, il existe toujours une toute petite matriochka quon ne peut ouvrir, et enn 0! = 1. Dans ce chapitre, nous aborderons deux sortes de rcursivit : la rcursivit des actions et celle des objets.

1 Mathmaticien 2 Non

amricain (1903-1995).

contredite jusqu aujourdhui.

162

Chapitre 15 Rcursivit

15.1
15.1.1

RCURSIVIT DES ACTIONS


Dnition

Dans les langages de programmation, la rcursivit des actions se dnit par des fonctions ou des procdures rcursives. Un sous-programme rcursif contiendra au moins un nonc dappel, direct ou non, lui-mme dans son corps. Une procdure (ou une fonction) rcursive P , dnie selon le modle ci-dessous, cre un nombre inni dincarnations de la procdure au moyen dun nombre ni dnoncs. P = C(Ei , P ) o C reprsente une composition dnoncs Ei .

15.1.2

Finitude

La dnition prcdente ne limite pas le nombre dappels rcursifs, et un programme crit selon ce modle ne pourra jamais sachever. Il est donc ncessaire de limiter le nombre des appels rcursifs. Lappel rcursif dune fonction ou dune procdure devra toujours apparatre dans un nonc conditionnel et sappliquer un sous-ensemble du problme rsoudre. P = si B alors C(Ei , P ) finsi ou bien P = C(Ei , si B alors P finsi) Toutefois, il faut tre sr que lexcution des instructions conduira tt ou tard la branche de lnonc conditionnel qui ne contient pas dappel rcursif. Comme pour les noncs itratifs, il est essentiel de prouver la nitude du sous-programme P . Pour cela, on lui associe un ou plusieurs paramtres qui dcrivent le domaine dapplication du sous-programme. Les valeurs de ces paramtres doivent voluer pour restreindre le domaine dapplication et tendre vers une valeur particulire qui arrtera les appels rcursifs. Le modle devient alors : Px = si B alors C(Ei , Px ) finsi ou bien Px = C(Ei , si B alors Px finsi) o x est une partie de x. Bien sr, x et x ne peuvent tre gaux, sinon, en dehors de tout effet de bord, les appels rcursifs seraient tous identiques et le sous-programme ne pourrait sachever.

15.1.3

criture rcursive des sous-programmes

Pour beaucoup de nophytes, lcriture rcursive des sous-programmes apparat tre une relle difcult, et bien souvent ceux-ci envisagent a priori une solution non rcursive, cest-dire base sur un algorithme itratif. Pourtant, certains pensent que litration est humaine

15.1

Rcursivit des actions

163

et la rcursivit divine. Au-del de cet aphorisme, lcriture rcursive est immdiate pour des algorithmes naturellement rcursifs, comme les dnitions mathmatiques par rcurrence, ou pour ceux qui manipulent des objets rcursifs (voir 15.2). Elle est aussi plus concise, et souvent plus claire que son quivalent itratif, mme si sa comprhension ncessite une certaine habitude. De plus, lanalyse de la complexit des algorithmes rcursifs est souvent plus simple mettre en uvre. Les dnitions par rcurrence des fonctions mathmatiques ont une transposition algorithmique vidente. Par exemple, les fonctions factorielle et bonacci3 sexpriment par rcurrence comme suit : fac(0) = 1 fac(n) = n fac(n 1), n > 0 b(1) = 1 b(2) = 1 b(n) = b(n 1) + b(n 2), n > 2 Lcriture des deux algorithmes est immdiate dans la mesure o il suft de respecter la dnition mathmatique de la fonction :
fonction factorielle(donne n : naturel) : nature {Antcdent : n 0} {Rle : calcule n! = n n-1!, avec 0! = 1} si n=0 alors rendre 1 sinon rendre n factorielle(n-1) finsi finfonc {factorielle} fonction fibonacci(donne n : naturel) : naturel {Antcdent : n>0} {Rle : calcule fib(n)= fib(n-1) + fib(n-2), pour n > 2 et avec fib(1) = 1 et fib(2) = 1} si n 2 alors rendre 1 sinon rendre fibonacci(n-1) + fibonacci(n-2) finsi finfonc {fibonacci}

Vous remarquerez que la nitude de ces deux fonctions est garantie par leur paramtre qui dcrot chaque appel rcursif, pour tendre vers un et zro, valeurs pour lesquelles la rcursivit sarrte. Le problme des tours de Hano, voir la gure 15.1, consiste dplacer n disques concentriques empils sur un premier axe A vers un deuxime axe B en se servant dun troisime axe intermdiaire C. La rgle exige quun seul disque peut tre dplac la fois, et quun disque ne peut tre pos que sur un axe vide ou sur un autre disque de diamtre suprieur. La lgende indique que pour une pile de 64 disques et raison dun dplacement de disque par
fonction fut propose en 1202 par le mathmaticien italien L EONARDO P ISANO (11751250), appel F I (une contraction de lius Bonacci), pour calculer le nombre annuel de couples de lapins que peut produire un couple initial, en supposant que tous les mois chaque nouveau couple produit un nouveau couple de lapins, qui deviendra son tour productif deux mois plus tard.
BONACCI
3 Cette

164

Chapitre 15 Rcursivit

B
F IG . 15.1 Les tours de Hano.

seconde, la n du monde aura lieu lorsque la pile sera entirement reconstitue ! Le nombre total de dplacements est exponentiel. Il est gal 2n 1. Lcriture rcursive de lalgorithme est trs lgante et trs concise. Pour placer, sa position nale, le plus grand disque il faut que laxe B soit vide, et quune tour, forme des n 1 restants, soit reconstitue sur laxe C. Le dplacement de ces n 1 disques se fait bien videmment par lintermdiaire de laxe B selon les rgles des tours de Hano, cest--dire de faon rcursive. Il suft ensuite de dplacer les n 1 disques de laxe C vers laxe B, toujours de faon rcursive, en se servant de laxe A comme axe intermdiaire.
procdure ToursdeHano(donnes n : nbdisques a, b, c : axes) {Rle : dplacer n disques concentriques de laxe a vers laxe b, en utilisant c comme axe intermdiaire} si n>0 alors {dplacer n-1 disques de a vers c, intermdiaire b} ToursdeHano(n-1,a,c,b) {dplacer le disque n de laxe a vers b} dplacer(n,a,b) {dplacer n-1 disques de c vers b, intermdiaire a} ToursdeHano(n-1,c,b,a) finsi finproc {ToursdeHano}

Le nombre de disques dplacs rcursivement diminue de un chaque appel rcursif et tend vers zro. Pour cette valeur particulire la rcursivit sarrte, ce qui garantit la nitude de lalgorithme. Prenons un dernier exemple. Nous dsirons crire une procdure qui crit sur la sortie standard la suite de chiffres que forme un entier naturel. Imaginons que le langage ne mette notre disposition quune procdure dcriture dun caractre et une fonction de conversion dun chiffre en caractre. Lalgorithme dcompose le nombre par divisions entires successives et procde lcriture des chiffres produits.
procdure crireChiffres(donne n : naturel) {Antcdent : n 0} {Rle : crit sur la sortie standard la suite de

15.1

Rcursivit des actions

165

chiffres qui forme lentier n} si n 10 alors crireChiffres(n/10) finsi crire(convertirEnCaractre(n mod 10)) finproc {crireChiffres}

La nitude de cet algorithme est assure pour tout entier positif. Les appels rcursifs sappliquent une suite dentiers naturels qui tend vers un nombre infrieur dix. Pour cette valeur, la rcursivit sarrte. Sa programmation en JAVA ne pose pas de difcult particulire :
public static void crireChiffres(int n) // Antcdent : n 0 // Rle : crit sur la sortie standard la suite de // chiffres qui forme lentier n { assert n>=0; if (n>=10) crireChiffres(n/10); // crire la conversion du chiffre en caractre System.out.print((char) (n%10 + 0 )); }

15.1.4

La pile dvalution

Lcriture itrative de la procdure crireChiffres ncessite de mmoriser les chiffres (par exemple dans un tableau) parce que la dcomposition par divisions successives produit les chiffres dans lordre inverse de celui souhait. Le calcul du rsultat est obtenu ensuite aprs un parcours lenvers de la squence de chiffres mmorise.
public static void crireChiffres(int n) { assert n>=0; final int maxchiffres=10; // 32 bits / 3 int[] chiffres=new int[maxchiffres]; int i=0; // dcomposer le nombre par divisions successives // mmoriser les chiffres dans le tableau do chiffres[i++]=n%10; while ((n/=10) != 0); // parcourir le tableau des chiffres en sens inverse // et crire la conversion de chaque chiffre en caractre while (i>0) System.out.print((char) (chiffres[--i] + (int) 0 )); }

Pourquoi la version rcursive se passe-t-elle du tableau ? En fait, la squence dappels rcursifs mmorise les chiffres du nombre dans une pile cache , la pile dvaluation du programme dans laquelle sempilent les zones locales des procdures et des fonctions. Le parcours lenvers de la squence de chiffres produite est obtenu automatiquement lorsquon

166

Chapitre 15 Rcursivit

retour de procdure crireChiffres(1) 1

crireChiffres(12)

crireChiffres(125)

crireChiffres(1257)

appels de procdure

contexte dappel

F IG . 15.2 Excution de crireChiffres(1257).

dpile la pile dvaluation, en n dexcution de chaque appel rcursif. La gure 15.2 montre la pile dvaluation pour lappel de la fonction crireChiffres(1257). gauche de la pile, les ches indiquent lempilement des zones locales produites par les appels rcursifs et sa droite est la valeur crite par la procdure une fois lexcution de chaque appel acheve. De nombreux algorithmes rcursifs se servent de la pile dvaluation pour mmoriser des donnes, en particulier les paramtres transmis par valeur du sous-programme, et pour rcuprer leur valeur lors du retour de lappel rcursif. La fonction fibonacci et la procdure ToursDeHano procdent de la sorte. En exercice, vous pouvez essayer de drouler la main la suite des appels rcursifs de ces fonctions, mais dune faon gnrale, la comprhension dun algorithme rcursif doit se faire de faon synthtique partir du cas gnral.

15.1.5

Quand ne pas utiliser la rcursivit ?

Une premire rponse cette question est quand lcriture itrative est vidente. Typiquement, cest le cas pour les fonctions factorielle et fibonacci et on leur prfrera les versions itratives suivantes :
fonction factorielle(donne n : naturel) : naturel {Rle : calcule n! = n n-1!, avec 0! = 1} variables i, fact de type naturel i 0 fact 1 tantque i<n faire {fact = i!} i i+1

15.1

Rcursivit des actions

167

fact fact i fintantque {i=n et fact=n!} rendre fact finfonc {factorielle} fonction fibonacci(donne n : naturel) : naturel {Rle : calcule fib(n)= fib(n-1) + fib(n-2), pour n>2 et avec fib(1) = 1 et fib(2) = 1} variables i, pred, succ de type naturel i 1 pred 1 succ 1 tantque i<n faire {pred=fib(i) et succ=fib(i+1)} i i+1 succ succ+pred pred succ-pred fintantque rendre pred finfonc {fibonacci}

Deuximement, lorsque la rcursivit est terminale, cest--dire lorsque la dernire instruction du sous-programme est lappel rcursif. Le processus rcursif est quivalent un processus itratif mettre en uvre avec un nonc tantque. Des sous-programmes de la forme : P = si B alors E P finsi P = E; si B alors P finsi sont quivalents : P = initialisation tantque B faire E fintantque Lcriture itrative de la fonction factorielle est une application directe de cette rgle de transformation. Troisimement, quand lefcacit en temps dexcution des programmes est en jeu. La rcursivit a un cot, celui des appels rcursifs des procdures ou des fonctions. Pour sen convaincre, comparez les temps dexcution des versions itrative et rcursive de fibonacci crites en JAVA avec n = 50 et n = 55. Sur un Intel Core 2, le calcul itratif est immdiat, moins dune milliseconde, alors que le calcul rcursif ncessite plus de deux minutes pour n = 50, et plus de vingt-trois minutes pour n = 55. Au del, ce sont des heures de calcul ! Les temps dexcution prcdents ne sont pas une surprise. Le calcul rcursif de
fibonacci est particulirement lourd : fibonacci(5) ncessite pas moins de 9 appels rcursifs, et fibonacci(50) plus de 25 milliards ! Le nombre thorique dappels est gal

168

Chapitre 15 Rcursivit

(2/ 5)1, 618n 1. La complexit de lalgorithmique rcursif est exponentielle. On lui prfrera donc celle linaire de la version itrative. Dune faon gnrale, et mme si sur les ordinateurs actuels les appels des sous-programmes sont efcaces, lorsque le nombre dappels rcursifs devient suprieur n log2 n, o n est le paramtre qui contrle les appels rcursifs, il est raisonnable de rechercher une solution itrative. De mme, lorsque la profondeur de rcursivit est suprieure n, on recherchera une solution itrative. Toutefois, il existe des situations o il est difcile de se prononcer parce que la solution itrative est loin dtre vidente. Regardons par exemple la fonction ackermann4 donne par la relation de rcurrence suivante : ackermann(0, n) ackermann(m, 0) ackermann(m, n) =n+1 = ackermann(m 1, 1) = ackermann(m 1, ackermann(m, n 1))

et dont la programmation en JAVA est donne par :


public static long ackermann(long m, long n) { if (m==0) return n+1; else if (n==0) return ackermann(m-1,1); else return ackermann(m-1,ackermann(m,n-1)); }

Il est clair que cette criture est trs coteuse en terme de nombre dappels rcursifs. La fonction Ackermann crot de faon exponentielle, mais plus rapidement encore que fibonacci. Toutefois, lcriture rcursive est immdiate, contrairement la version itrative. En exercice, je vous laisse chercher la solution itrative. Sachez que pour toute fonction rcursive, il existe toujours une solution itrative, ne serait-ce quen simulant la pile des appels rcursifs. Notez bien que cette fonction ne contient que deux appels rcursifs, et toute la difcult est de drcursiver le premier appel.

15.1.6

Rcursivit directe et croise

Les procdures prcdentes taient bties sur le mme modle. Lappel rcursif est plac directement dans le corps de la procdure :
procdure P ... P ... finproc {P}

Mais un sous-programme P peut faire appel un ou plusieurs autres sous-programmes qui, en dernier lieu, fait appel au sous-programme P initial. On parle alors de rcursivit indirecte. Lorsque seulement deux sous-programmes sont mis en jeu, on parle de rcursivit croise :
4 W.

ACKERMANN, mathmaticien allemand (1896-1962).

15.1

Rcursivit des actions

169

procdure P1 ... P2 ... finproc {P1} procdure P2 ... P1 ... finproc {P2}

Lanalyse des expressions arithmtiques en notation inxe est un bon exemple de rcursivit indirecte. La grammaire donne ci-dessous dcrit des expressions formes doprateurs additifs et multiplicatifs, et doprandes. La grammaire xe la priorit des oprateurs : les oprateurs additifs sont moins prioritaires que les oprateurs multiplicatifs.
<expression> = <terme> | <expression> <op-add> <terme> <terme> = <facteur> | <terme> <op-mult> <facteur> <facteur> = <variable> | <entier> | ( <expression> )

Chaque entit, <expression>, <terme> ou <facteur> se dcrit par une procdure. La rcursivit indirecte a lieu dans <facteur> qui appelle rcursivement <expression> pour analyser des expressions parenthses. La rcursivit indirecte apparat aussi dans les courbes de H ILBERT5 . Ces courbes sont telles que toute courbe Hi sexprime en fonction dune courbe Hi1 . Plus prcisment, chaque courbe Hi consiste en lutilisation de quatre parties ( lchelle 1/2) de Hi1 . Ces quatre parties A, B, C et D sont relies entre elles par trois segments de droite de la faon suivante : A(i) : D(i-1) A(i-1) A(i-1) B(i-1) B(i) : C(i-1) B(i-1) B(i-1) A(i-1) C(i) : B(i-1) C(i-1) C(i-1) D(i-1) D(i) : A(i-1) D(i-1) D(i-1) C(i-1) Ci-dessus, les ches indiquent lorientation des segments de droite qui relient les quatre courbes de niveau moins un. La gure 15.3 reprsente la superposition des courbes de niveau un cinq. Si lon possde une fonction tracer qui trace un segment de droite dans le plan partir de la position courante jusquau point de coordonnes (x,y), la procdure A scrit :
procdure A(donne i: naturel) {Rle : tracer la partie A de la courbe de Hilbert de niveau i h longueur du segment qui relie les courbes de niveau i-1} si i>0 alors D(i-1) x x-h tracer(x,y) A(i-1) y y-h tracer(x,y) A(i-1) x x+h tracer(x,y) B(i-1) finsi finproc {A}

Lcriture des procdures B, C et D, bties sur ce modle, est immdiate.


5 DAVID H ILBERT , mathmaticien allemand (18621943), proposa cette courbe en 1890. Ce type de gure gomtrique est plus connu aujourdhui sous le nom de fractale.

170

Chapitre 15 Rcursivit

F IG . 15.3 Courbes de Hilbert de niveau 5.

15.2

RCURSIVIT DES OBJETS

linstar de la rcursivit des actions, un objet rcursif est un objet qui contient un ou plusieurs composants du mme type que lui. La rcursivit des objets peut tre galement indirecte. Imaginons que lon veuille reprsenter la gnalogie dun individu. En plus de son identit, il est ncessaire de connatre son ascendance, cest--dire larbre gnalogique de sa mre et de son pre. Il est clair que le type Arbre Gnalogique que nous sommes amens dnir est rcursif :
classe Arbre Gnalogique prnom type chane de caractres mre, pre type Arbre Gnalogique finclasse {Arbre Gnalogique}

Conceptuellement, une telle dclaration dcrit une structure innie, mais en gnral les langages de programmation ne permettent pas cette forme dauto-inclusion des objets. Contrairement la rcursivit des actions, celle des objets ne cre pas automatiquement une innit dincarnations dobjets. Cest au programmeur de crer explicitement chacune de ces incarnations et de les relier entre elles. La caractristique fondamentale des objets rcursifs est leur nature dynamique. Les objets que nous avons tudis jusqu prsent possdaient tous une taille xe. La taille des objets rcursifs pourra, quant elle, varier au cours de lexcution du programme. Pour mettre en uvre la rcursivit des objets, les langages de programmation proposent des outils qui permettent de crer dynamiquement un objet du type voulu et daccder cet objet.

15.2

Rcursivit des objets

171

Louise

Mahaba

Paul

Monique

Maud

Dhakwan

Jacques

F IG . 15.4 La gnalogie de Louise.

Des langages comme C ou PASCAL ne permettent pas une dnition directement rcursive des types, mais lautorisent au moyen de pointeurs. Ils mettent la disposition du programmeur des fonctions dallocation mmoire, comme par exemple malloc et new, pour crer dynamiquement les incarnations des objets, auxquelles il accde par lintermdiaire des pointeurs. Pour de nombreuses raisons, mais en particulier pour des raisons de abilit de construction des programmes, les langages de programmation modernes ont abandonn la notion de pointeur. Cest le cas de JAVA qui permet des dnitions dobjet vraiment rcursives. Ainsi, le type Arbre Gnalogique sera dclar en JAVA comme suit :
class ArbreGnalogique { String prnom; // dfinition de lascendance ArbreGnalogique mre, pre; // le constructeur ArbreGnalogique(String s) { prnom=s; } } // ArbreGnalogique

Cette dnition est possible en JAVA car les attributs mre et pre sont des rfrences des objets de type ArbreGnalogique et non pas lobjet lui-mme. Larbre gnalogique de Louise, donn par la gure 15.4, est produit par la squence dinstructions suivante :
ArbreGnalogique ag; ag = new ArbreGnalogique(" Louise "); ag.mre = new ArbreGnalogique("Mahaba"); ag.pre = new ArbreGnalogique(" Paul "); ag.mre.mre = new ArbreGnalogique("Monique"); ag.pre.mre = new ArbreGnalogique("Maud"); ag.pre.pre = new ArbreGnalogique("Dhakwan"); ag.pre.pre.pre = new ArbreGnalogique(" Jacques ");

172

Chapitre 15 Rcursivit

111111111 000000000 111111111 000000000 tas 111111111 000000000 111111111 000000000


espace libre

111111111 000000000 111111111 000000000 pile dvaluation 111111111 000000000 111111111 000000000 111111111 000000000 111111111 000000000
111111111 000000000 111111111 000000000 zone globale 111111111 000000000 111111111 000000000 111111111 000000000 111111111 000000000
F IG . 15.5 Organisation de la mmoire.

Chacun des ascendants, lorsquil est connu, est cr grce loprateur new et est reli sa propre ascendance par lintermdiaire des rfrences. Celles-ci sont reprsentes sur la gure 15.4 par des ches. Le symbole de la terre des lectriciens indique la n de la rcursivit. En JAVA, il correspond une rfrence gale la constante null. Les supports dexcution des langages de programmation placent les objets dynamiques dans une zone spciale, appele tas. La gure 15.5 montre lorganisation classique de la mmoire lors de lexcution dun programme. La zone globale est une zone de taille xe qui contient des constantes et des donnes globales du programme. La pile dvaluation, dont la taille varie au cours de lexcution du programme, contient lempilement des zones locales des sous-programmes appels avec leur pile dvaluation des expressions. Enn, le tas contient les objets allous dynamiquement par le programme. Le tas crot en direction de la pile dvaluation. Sils se rencontrent, le programme sarrte faute de place mmoire pour sexcuter. Selon les langages, la suppression des objets dynamiques, cest--dire la libration de la place mmoire quils occupent dans le tas, peut tre la charge du programmeur, ou laisser au support dexcution. La suppression des objets dynamiques est une source de nombreuses erreurs, et il est prfrable que le langage automatise la destruction des objets qui ne servent plus. Cest le choix fait par JAVA. Dans les prochains chapitres, nous aurons souvent loccasion de mettre en pratique des dnitions dobjet rcursif. De nombreuses structures de donnes que nous aborderons seront dnies de faon rcursive et les algorithmes qui les manipuleront seront eux-mmes naturellement rcursifs.

15.3

Exercices

173

15.3

EXERCICES

Exercice 15.1. Programmez en JAVA les algorithmes fibonacci et ToursdeHano donns la page 163. Exercice 15.2. crivez en JAVA et de faon rcursive la fonction puissance qui lve un nombre rel la puissance n (entire positive ou nulle). Note : lorsque n est pair, pensez llvation au carr. Exercice 15.3. En vous inspirant de la procdure crireChiffres, crivez de faon rcursive et itrative la fonction convertirRomain qui retourne la reprsentation romaine dun entier. On rappelle que les nombres romains sont dcoups en quatre tranches : milliers, centaines, dizaines et units (dans cet ordre). Dans chaque tranche, on crit de zro quatre chiffres et jamais plus de trois chiffres identiques conscutifs. Les tranches nulles ne sont pas reprsentes. Les chiffres romains sont I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, et M = 1000. Par exemple, 49 = XLIX, 703 = DCCIII et 2000 = M M . Exercice 15.4. crivez une procdure qui engendre les n! permutations de n lments a1 , , an . La tche consistant engendrer les n! permutations des lments a1 , , an peut tre dcompose en n sous-tches de gnration de toutes les permutations de a1 , , an1 suivies de an , avec change de ai et an dans la ie sous-tche. Exercice 15.5. crivez un programme qui recopie sur la sortie standard un chier de texte. Lorsquil reconnat dans le chier une directive de la forme !nom-de-chier, il inclut le contenu du chier en lieu et place de la directive. videmment, un chier inclus peut contenir une ou plusieurs directives dinclusion. Exercice 15.6. Soit une fonction continue f dnie sur un intervalle [a, b]. On cherche trouver un zro de f , cest--dire un rel x [a, b] tel que f (x) = 0. Si la fonction admet plusieurs zros, nimporte lequel fera laffaire. Sil ny en a pas, il faudra le signaler. Dans le cas o f (a).f (b) < 0, on est sr de la prsence dun zro. Lorsque f (a).f (b) > 0, il faut rechercher un sous-intervalle [, ], tel que f ().f () < 0. Lalgorithme procde par dichotomie, cest--dire quil va diviser lintervalle de recherche en deux moitis chaque tape. Si lun des deux nouveaux intervalles, par exemple [, ], est tel que f ().f () < 0, on sait quil contient un zro puisque la fonction est continue : on poursuivra alors la recherche dans cet intervalle. En revanche, si les deux demi intervalles sont tels que f a le mme signe aux deux extrmits, la solution, si elle existe, sera dans lun ou lautre de ces deux demi intervalles. Dans ce cas, on prendra arbitrairement lun des deux demi intervalles pour continuer la recherche ; en cas dchec on reprendra le deuxime demi intervalle qui avait t provisoirement nglig. crivez de faon rcursive lalgorithme de recherche dun zro, prs, de la fonction f . Exercice 15.7. partir de la petite grammaire dexpression donne la page 169, crivez en JAVA un valuateur dexpressions arithmtiques inxes. Pour simplier, vous ne traiterez pas la notion de variable dans facteur.

Chapitre 16

Structures de donnes

Le terme structure de donnes dsigne une composition de donnes unies par une mme smantique. Mais, cette smantique ne se rduit pas celle des types (lmentaires ou structurs) des langages de programmation utiliss pour programmer la structure de donnes. Ds le dbut des annes soixante-dix, C.A.R. H OARE [Hoa72] mettait en avant lide quune donne reprsente avant tout une abstraction du monde rel dnie en terme de structures abstraites, et qui nest dailleurs pas ncessairement mise en uvre laide dun langage de programmation particulier. Dune faon plus gnrale, un programme peut tre lui-mme modlis en termes de donnes abstraites munies doprations abstraites. Ces rexions ont conduit dnir une structure de donnes comme une donne abstraite, dont le comportement est modlis par des oprations abstraites. Cest partir du milieu des annes soixante-dix que la thorie des types abstraits algbriques est apparue pour dcrire les structures de donnes. Dnis en termes de signature, les types abstraits doivent dune part, garantir leur indpendance vis--vis de toute mise en uvre particulire, et dautre part, offrir un support de preuve de la validit de leurs oprations. Dans ce chapitre, nous verrons comment spcier une structure de donnes laide dun type abstrait, et comment limplanter dans un langage de programmation particulier comme JAVA. Dans les chapitres suivants, nous prsenterons plusieurs structures de donnes fondamentales, que tout informaticien doit connatre. Il sagit des structures linaires, de la structure de graphe, des structures arborescentes et des tables.

176

Chapitre 16 Structures de donnes

16.1

DFINITION DUN TYPE ABSTRAIT

Un type abstrait est dcrit par sa signature qui comprend : une dclaration des ensembles dnis et utiliss ; une description fonctionnelle des oprations ; une description axiomatique de la smantique des oprations. Dans ce qui suit, nous dnirons (partiellement) les types abstraits EntierNaturel et Ensemble. Le premier dcrit lensemble N des entiers naturels et le second des ensembles dlments quelconques. Dclaration des ensembles Cette dclaration indique le nom du type abstrait dnir, ainsi que certaines constantes qui jouent un rle particulier. La notation : EntierNaturel. 0 EntierNaturel dclare le type abstrait EntierNaturel des entiers naturels, qui possde un lment particulier dnot 0. La dclaration ensembliste de certains types abstraits ncessite de mentionner dautres types abstraits. Ces derniers sont introduits par le mot-cl utilise. Le type abstrait Ensemble utilise deux autres types abstraits, boolen et E. Il possde la dnition suivante : Ensemble utilise E, boolen. Ensemble o E dnit les lments dun ensemble, et un ensemble vide. Remarquez que les types abstraits utiliss nont pas ncessairement besoin dtre au pralable entirement dnis. Pour la spcication du type abstrait Ensemble de ce chapitre, seule la connaissance des deux lments vrai et faux de lensemble des boolens nous est utile. Description fonctionnelle La dnition fonctionnelle prsente les signatures des oprations du type abstrait. Pour chaque opration, sa signature indique son nom et ses ensembles de dpart et darrive. Lensemble des entiers naturels peut tre dcrit laide de lopration succ qui, pour un entier naturel, fournit son successeur. Il est galement possible de dnir les oprations arithmtiques + et . succ + : EntierNaturel : EntierNaturel EntierNaturel : EntierNaturel EntierNaturel EntierNaturel EntierNaturel EntierNaturel

Si on munit le type abstrait Ensemble des oprations est-vide ?, , ajouter et union, le type abstrait contiendra les signatures suivantes :

16.1

Dnition dun type abstrait

177

est-vide? : : ajouter : enlever : union : Description axiomatique

Ensemble Ensemble E Ensemble E Ensemble E Ensemble Ensemble

boolen boolen Ensemble Ensemble Ensemble

La dnition axiomatique dcrit la smantique des oprations du type abstrait. Il est clair que les dnitions ensembliste et fonctionnelle prcdentes ne sufsent pas exprimer ce quest le type EntierNaturel ou le type Ensemble. Le choix des noms des oprations nous claire, mais il existe par exemple une innit de fonctions de N dans N, et pour linstant, rien ne distingue rellement la smantique des oprations + et . Il faut donc spcier de faon formelle les proprits des oprations du type abstrait, ainsi que leur domaine de dnition lorsquelles correspondent des fonctions partielles, comme par exemple enlever. Pour cela, on utilise des axiomes qui mettent en jeu les ensembles et les oprations. Pour le type EntierNaturel, nous pouvons utiliser les axiomes proposs par G. P EANO 1 au sicle dernier. (1) (2) (3) (4) (5) (6) (7) x EntierNaturel , x , succ(x) = x x, x EntierNaturel , x = x succ(x) = succ(x ) x EntierNaturel , succ(x) = 0 x EntierNaturel , x + 0 = x x, y EntierNaturel , x + succ(y) = succ(x + y) x EntierNaturel , x 0 = 0 x, y EntierNaturel , x succ(y) = x + x y

Le premier axiome indique que tout entier naturel possde un successeur. Le second, que deux entiers naturels distincts possdent deux successeurs distincts. Le troisime axiome prcise que 0 nest le successeur daucun entier naturel. Enn, les quatre dernires spcient les oprations + et . Grce ces axiomes, il est dmontr que tous les thormes de larithmtique de G. P EANO sont vrais pour les entiers naturels. Les axiomes suivants dcrivent les oprations du type abstrait Ensemble : (1) (2) (3) (4) est-vide?() = vrai x E, e Ensemble, est-vide?(ajouter(e, x)) = faux x E, x = faux x, y E, e Ensemble, x = y y ajouter(e, x) = vrai x = y y ajouter(e, x) = y e (5) x, y E, e Ensemble, x = y y enlever(e, x) = faux x = y y enlever(e, x) = y e
1 G IUSEPPE

P EANO, mathmaticien italien (1858-1932).

178

Chapitre 16 Structures de donnes

(6) x E, e Ensemble, x e e Ensemble, e = enlever(e, x) (7) x union(e, e ) x e ou x e Notez que laxiome (5) impose la prsence dans lensemble de llment retirer. La fonction enlever est une fonction partielle, et cet axiome en prcise le domaine de dnition. Une des principales difcults de la dnition axiomatique est de sassurer, dune part, de sa consistance, cest--dire quil ny a pas daxiomes qui se contredisent, et dautre part, de sa compltude, cest--dire que les axiomes dnissent entirement le type abstrait. [FGS90] distingue deux types doprations : les oprations internes, qui rendent un rsultat de lensemble dni, et les observateurs, qui rendent un rsultat de lensemble prdni, et propose de tester la compltude dun type abstrait en vriant si lon peut dduire de ses axiomes le rsultat de chaque observateur sur son domaine de dnition. Pour garantir la consistance, il suft alors de sassurer que chacune de ces valeurs est unique.

16.2

LIMPLANTATION DUN TYPE ABSTRAIT

Limplantation est la faon dont le type abstrait est programm dans un langage particulier. Il est vident que limplantation doit respecter la dnition formelle du type abstrait pour tre valide. Certains langages de programmation, comme A LPHARD ou E IFFEL, incluent des outils qui permettent de spcier et de vrier automatiquement les axiomes, cest--dire de contrler si les oprations du type abstrait respectent, au cours de leur utilisation, ses proprits algbriques. Limplantation consiste donc choisir les structures de donnes concrtes, cest--dire des types du langage dcriture pour reprsenter les ensembles dnis par le type abstrait, et de rdiger le corps des diffrentes fonctions qui manipuleront ces types. Dune faon gnrale, les oprations des types abstraits correspondent des sous-programmes de petite taille qui seront donc faciles mettre au point et maintenir. Pour un type abstrait donn, plusieurs implantations possibles peuvent tre dveloppes. Le choix dimplantation du type abstrait variera selon lutilisation qui en est faite et aura une inuence sur la complexit des oprations. Le concept de classe des langages objets facilite la programmation des types abstraits dans la mesure o chaque objet porte ses propres donnes et les oprations qui les manipulent. Notez toutefois que les oprations dun type abstrait sont associes lensemble, alors quelles le sont lobjet dans le modle de programmation objets. La majorit des langages objets permet mme de conserver la distinction entre la dnition abstraite du type et son implantation grce aux notions de classe abstraite ou dinterface. En JAVA, linterface suivante reprsente la dnition fonctionnelle du type abstrait Entier Naturel :
public interface EntierNaturel { public EntierNaturel succ(); public EntierNaturel plus(EntierNaturel n); public EntierNaturel mult(EntierNaturel n); }

16.2

Limplantation dun type abstrait

179

Dans la mesure o le langage JAVA nautorise pas la surcharge des oprateurs, les symboles
+ et * nont pu tre utiliss et les oprations daddition et de multiplication ont t nommes.

La dnition fonctionnelle du type abstrait Ensemble correspondra la dclaration de linterface gnrique suivante :
public interface Ensemble<E> { public boolean estVide(); public boolean dans(E x); public void ajouter(E x); public void enlever(E x) throws ExceptionlmentAbsent; public Ensemble<E> union(Ensemble<E> x); }

Notez que la mthode enlever met une exception si llment x retirer nest pas prsent dans lensemble. Lexception traduit laxiome (5) du type abstrait. Dune faon gnrale, les exceptions serviront la dnition des fonctions partielles des types abstraits. La dnition du type abstrait Ensemble nimpose aucune restriction sur la nature des lments des ensembles. Ceux-ci peuvent tre diffrents ou semblables. Les oprations dappartenance ou dunion doivent sappliquer aussi bien des ensembles dentiers qu des ensembles de Rectangle, ou encore des ensembles densembles de chanes de caractres. Limplantation du type abstrait doit tre alors gnrique, cest--dire quelle doit permettre de manipuler des lments de nimporte quel type. Souvent, il est mme souhaitable dimposer que tous les lments soient dun type donn. De nombreux langages de programmation (A DA, C++, E IFFEL, etc.) incluent dans leur dnition la notion de gnricit et proposent des mcanismes de construction de types gnriques. Depuis sa version 5.0, JAVA inclut la gnricit. JAVA offre la dnition de types gnriques auxquels on passe en paramtre le type dsir. On ne va pas dcrire ici tous les dtails de cette notion du langage JAVA. Le lecteur intress pourra se reporter [GR08]. Ici, la dclaration de linterface Ensemble est paramtre sur le type des lments de lensemble. La mise en uvre des oprations dpendra des types de donnes choisis pour implanter le type abstrait. Pour un ensemble, il est possible de choisir un arbre ou une liste, eux-mmes implants laide de tableau ou dlments chans. Limplantation de linterface Ensemble aura par exemple la forme suivante :
public class EnsembleListe<E> implements Ensemble<E> { Liste<E> l; public EnsembleListe() { // Le constructeur ... } public boolean estVide() { ... } public boolean dans(E x) { ... }

180

Chapitre 16 Structures de donnes

public void ajouter(E x) { ... } public void enlever(E x) throws ExceptionlmentAbsent { ... } ... } // fin classe EnsembleListe

Dans les dclarations de linterface Ensemble et de la classe EnsembleListe, le nom E est une sorte de paramtre formel qui permet de paramtrer linterface et la classe sur un type donn. Le compilateur pourra ainsi contrler que tous les lments dun ensemble sont de mme type. Notez que dans la partie implantation dun type abstrait, le programmeur devra bien prendre soin dinterdire laccs aux donnes concrtes, et de rendre publiques les oprations du type abstrait.

16.3

UTILISATION DU TYPE ABSTRAIT

Puisque la dnition dun type abstrait est indpendante de toute implantation particulire, lutilisation du type abstrait devra se faire exclusivement par lintermdiaire des oprations qui lui sont associes et en aucun cas en tenant compte de son implantation. Dailleurs certains langages de programmation peuvent vous limposer, mais ce nest malheureusement pas le cas de tous les langages de programmation et cest alors au programmeur de faire preuve de rigueur ! Les en-ttes des fonctions et des procdures du type abstrait et les afrmations qui dnissent leur rle reprsentent linterface entre lutilisateur et le type abstrait. Ceci permet videmment de manipuler le type abstrait sans mme que son implantation soit dnie, mais aussi de rendre son utilisation indpendante vis--vis de tout changement dimplantation. Des dclarations de variables de type Ensemble pourront scrire en JAVA comme suit :
Ensemble<Integer> e1 = new EnsembleListe<Integer>(); Ensemble<Rectangle> e2 = new EnsembleListe<Rectangle>(); Ensemble<Ensemble<String>> e3 = new EnsembleListe<Ensemble<String>>(); Ensemble e4 = new EnsembleListe();

Dans ces dclarations, e1 est un ensemble dInteger, e2 un ensemble de Rectangle, et e3 un ensemble densembles de String. En revanche, pour la dernire dclaration, il ny a pas de contrainte sur le type des lments de lensemble e4 et ces lments pourront donc tre de type quelconque. Lutilisation du type abstrait se fera exclusivement par lintermdiaire des mthodes dnies dans son interface. Par exemple, les ajouts suivants seront valides quelle que soit limplantation choisie pour le type Ensemble.
e1.ajouter(123); e2.ajouter(new Rectangle(2,5));

16.3

Utilisation du type abstrait

181

e3.ajouter(new EnsembleListe<String>()); e4.ajouter(123.45);

Lexemple suivant montre lcriture dune mthode gnrique qui permet de manipuler le type gnrique des lmnts dun Ensemble.
<E> void uneMthode(Ensemble<E> e) { E x; ... if (e.dans(x)) { // x e ... } ... }

Enn pour conclure, on peut ajouter que si le type abstrait est juste et valid, il y a plus de chances que son utilisation exclusivement laide de ses fonctions soit elle aussi juste.

Chapitre 17

Structures linaires

Les structures linaires sont un des modles de donnes les plus lmentaires et utiliss dans les programmes informatiques. Elles organisent les donnes sous forme de squence non ordonne dlments accessibles de faon squentielle. Tout lment dune squence, sauf le dernier, possde un successeur. Une squence s constitue de n lments sera dnote comme suit : s = < e1 e2 e3 . . . en > et la squence vide : s = <> Les oprations dajout et de suppression dlments sont les oprations de base des structures linaires. Selon la faon dont procdent ces oprations, nous distinguerons diffrentes sortes de structures linaires. Les listes autorisent des ajouts et des suppressions dlments nimporte o dans la squence, alors que les piles, les les et les dques ne les permettent quaux extrmits. On considre que les piles, les les et les dques sont des formes particulires de liste linaire. Dans ce chapitre, nous commencerons par prsenter la forme gnrale, puis nous tudierons les trois formes particulires de liste.

17.1

LES LISTES

La liste dnit une forme gnrale de squence. Une liste est une squence nie dlments reprs selon leur rang. Sil ny a pas de relation dordre sur lensemble des lments de la squence, il en existe une sur le rang. Le rang du premier lment est 1, le rang du second est

184

Chapitre 17 Structures linaires

2, et ainsi de suite. Lajout et la suppression dun lment peut se faire nimporte quel rang valide de la liste.

17.1.1

Dnition abstraite

Ensembles Liste est lensemble des listes linaires non ordonnes dont les lments appartiennent un ensemble E quelconque. Lensemble des entiers reprsente le rang des lments. La constante listevide est la liste vide. Liste utilise E, naturel et entier listevide Liste Description fonctionnelle Le type abstrait Liste dnit les quatre oprations de base suivantes : longueur : i`me e : supprimer : ajouter : Liste Liste entier Liste entier Liste entier E naturel E Liste Liste

Lopration longueur renvoie le nombre dlments de la liste. Lopration ime retourne llment dun rang donn. Enn, supprimer (resp. ajouter) supprime (resp. ajoute) un lment un rang donn. Description axiomatique Les axiomes suivants dcrivent les quatre oprations applicables sur les listes. La longueur dune liste vide est gale zro. Lajout dun lment dans la liste augmente sa longueur de un, et sa suppression la rduit de un. l Liste, et e E (1) longueur(listevide) = 0 (2) r, 1 r longueur(l), longueur(supprimer(l, r)) = longueur(l) 1 (3) r, 1 r longueur(l) + 1, longueur(ajouter(l, r, e)) = longueur(l) + 1 Lopration ime renvoie llment de rang r, et nest dnie que si le rang est valide. (4) r, r < 1 et r > longueur(l), e, e = i`me(l, r) e

Lopration supprimer retire un lment qui appartient la liste, cest--dire dont le rang est compris entre un et la longueur de la liste. Les axiomes suivants indiquent que le rang des lments droite de llment supprim est dcrment de un. (5) r, 1 r longueur(l) et 1 i < r i`me(supprimer(l, r), i) = i`me(l, i) e e (6) r, 1 r longueur(l) et r i longueur(l) 1, i`me(supprimer(l, r), i) = i`me(l, i + 1) e e

17.1

Les listes

185

(7) r, r < 1 et r > longueur(l),

l , l = supprimer(l, r)

Lopration ajouter insre un lment un rang compris entre un et la longueur de la liste plus un. Le rang des lments la droite du rang dinsertion est incrment de un. (8) r, 1 r longueur(l) + 1 et 1 i < r, i`me(ajouter(l, r, e), i) = i`me(l, i) e e (9) r, 1 r longueur(l) + 1 et r = i, i`me(ajouter(l, r, e), i) = e e (10) r, 1 r longueur(l) + 1 et r < i longueur(l) + 1, i`me(ajouter(l, r, e), i) = i`me(l, i 1) e e (11) r, r < 1 et r > longueur(l) + 1, l , l = ajouter(l, r, e)

17.1.2

Limplantation en Java

La description fonctionnelle du type abstrait Liste est traduite en JAVA par linterface gnrique suivante :
public interface Liste<E> { public int longueur(); public E ime(int r) throws RangInvalideException; public void supprimer(int r) throws RangInvalideException; public void ajouter(int r, E e) throws RangInvalideException; }

Les mthodes daccs aux lments de la liste peuvent lever une exception si elles tentent daccder un rang invalide. Cette exception, RangInvalideException, est simplement dnie par la dclaration :
public class RangInvalideException extends RuntimeException { public RangInvalideException() { super(); } }

An dinsister sur lindpendance du type abstrait vis--vis de son implantation, nous prsenterons successivement deux sortes dimplantation. La premire utilise des tableaux et la seconde des structures chanes. Les tableaux offrent une reprsentation contigu des lments, et permettent un accs direct aux lments qui les composent, mais ont comme principal inconvnient de xer la taille de la structure de donnes. Par exemple, si pour reprsenter une liste, on dclare un tableau de cent composants alors quelle que soit la longueur effective de la liste, lencombrement mmoire utilis sera celui des cent lments. Au contraire, les structures chanes sont des structures dynamiques qui permettent dadapter la taille de la structure de donnes au nombre effectif dlments. Lespace mmoire ncessaire pour mmoriser un lment est plus important, et le temps daccs aux lments est en gnral plus coteux parce quil a lieu de faon indirecte.

186

Chapitre 17 Structures linaires

Utilisation dun tableau La mthode qui vient en premier lesprit, lorsquon mmorise les lments dune liste dans un tableau, est de conserver systmatiquement le premier lment la premire place du tableau, et de ne faire varier quun indice de n de liste. La gure 17.1 montre une squence de cinq entiers place dans un tableau nomm lments. Lattribut lg, qui indique la longueur de la liste, donne galement lindice de n de liste.
lments.length1 ... ...

0 lments

13

23

182 100 . . .

lg = 5

F IG . 17.1 Une liste dans un tableau.

Lalgorithme de lopration ime est trs simple, puisque le tableau permet un accs direct llment de rang r. La complexit de cet algorithme est donc O(1). Notez que pour accder un lment de la liste lantcdent de lopration doit tre vri.
Algorithme ime(r) {Antcdent : 1 r longueur(l)} rendre lments[r-1] {les valeurs dbutent lindice 0}

Lopration de suppression dun lment de la liste provoque un dcalage des lments qui se situent droite du rang de suppression. Pour une liste de n lments, la complexit de cette opration est O(n), et lalgorithme qui la dcrit est le suivant :
Algorithme supprimer(r) {Antcdent : 1 r longueur(l)} pourtout i de r lg faire lments[i-1] lments[i] finpour lg lg-1

Lopration dajout dun lment e au rang r consiste dcaler dune position vers la droite tous les lments partir du rang r. Le nouvel lment est insr au rang r. Dans la plupart des langages de programmation, une dclaration de tableau xe sa taille la compilation. Quel que soit le constructeur choisi, un objet, instance de la classe ListeTableau, possdera un nombre dlments xe. Ceci contraint la mthode ajouter vrier si le tableau lments dispose dune place libre avant dajouter un nouvel lment. Comme pour lopration de suppression, la complexit de cet algorithme est O(n). Lalgorithme est le suivant :
Algorithme ajouter(r, e) {Antcdent : 1 r longueur(l)+1 et longueur(l)+1<lments.length}
pourtout i de lg-1 r-1 faire

17.1

Les listes

187

lments[i+1] lments[i] finpour {r-1 est lindice dinsertion} lments[r-1] e lg lg+1

Remarquez que lantcdent spcie que le tableau doit possder une place libre pour llment ajouter. Sil nest pas vri, cela se traduira en JAVA par lexception ListePleineException. Celle-ci nest pas dnie par le type abstrait, mais est induite par le choix des donnes concrtes pour son implantation. La dnition de lexception ListePleineException est semblable celle de ListeVideException :
public class ListePleineException extends RuntimeException { public ListePleineException() { super(); } }

La classe gnrique ListeTableau suivante donne limplantation complte dune liste laide dun tableau gr selon les algorithmes prcdents :
public class ListeTableau<E> implements Liste<E> { protected static final int MAXLM=100; protected int lg; protected E [] lments; public ListeTableau() { this(MAXLM); } public ListeTableau(int n) { lments = (E[]) new Object[n]; } public int longueur() { return lg; } public E ime(int r) throws RangInvalideException { if (r<1 || r>lg) throw new RangInvalideException(); return lments[r-1]; } public void supprimer(int r) throws RangInvalideException { if (r<1 || r>lg) throw new RangInvalideException() // dcaler les lments vers la gauche ; for (int i=r; i<lg; i++) lments[i-1]=lments[i]; lg--; } public void ajouter(int r, E e) throws RangInvalideException { if (lg==lments.length) throw new ListePleineException(); if (r<1 || r>lg+1) throw new RangInvalideException();

188

Chapitre 17 Structures linaires

// dcaler les lments vers la droite for (int i=lg; i>=r; i--) lments[i]=lments[i-1]; // r-1 est lindice de llment ajouter lments[r-1]=e; lg++; } } // fin classe ListeTableau

Notez que la gnricit de JAVA ne permet pas de crer dynamiquement des objets du type gnrique dans la mesure o toute trace du type gnrique a disparu lexcution. Cest pour cela que dans le constructeur, on cre des lments de tableau de type Object qui sont ensuite convertis explicitement en E. Dautre part, il est important de remarquer que la suppression de llment de tte est trs coteuse puisquelle provoque un dcalage de tous les lments de la liste. Ainsi pour des raisons defcacit, il sera prfrable de grer le tableau de faon circulaire. La gestion circulaire du tableau se fait laide de deux indices : un indice de tte qui dsigne le premier lment de la liste, et un indice de queue qui indique lemplacement libre aprs le dernier lment de la liste. Pour un tableau du langage JAVA, ces deux indices ont pour valeur initiale 0, et lindice du dernier composant est length-1. Les indices de tte ou de queue sont incrments ou dcrments de un chaque ajout ou suppression. Lorsquon incrmente un indice en n de tableau, sa prochaine valeur est alors lindice du premier composant du tableau :
tte = tte==lments.length-1 ? 0 : ++tte; queue = queue==lments.length-1 ? 0 : ++queue;

De mme, lorsquon dcrmente un indice en dbut de tableau, sa prochaine valeur est alors lindice du dernier composant du tableau :
tte = tte==0 ? lments.length-1 : --tte; queue = queue==0 ? lments.length-1 : --queue;

Notez que lindice de tte ne prcde pas ncessairement lindice de queue, et quau gr des ajouts et des suppressions lindice de tte peut tre aussi bien infrieur que suprieur lindice de queue. La gure 17.2 donne deux dispositions possibles de la squence < 5 20 4 9 45 > dans le tableau lments.
tte lments 5 20 4 9 45 queue

queue lments 4 9 45

tte 5 20

F IG . 17.2 Gestion circulaire dun tableau.

17.1

Les listes

189

Dans le cas gnral de la suppression ou de lajout dun lment qui nest pas situ lune des extrmits de la liste, le dcalage dune partie des lments est ncessaire comme dans la mthode de gestion du tableau prcdente. Ce dcalage est lui-mme circulaire et se fait modulo la taille du tableau. Lindice dun lment de rang r est gal tte+r-1. Nous dnissons la classe gnrique ListeTableauCirculaire en remplaant les mthodes ime, supprimer et ajouter par celles donnes ci-dessous :
public E ime(int r) throws RangInvalideException { if (r<1 || r>lg) throw new RangInvalideException(); return lments[(tte+r-1) % lments.length]; } public void supprimer(int r) throws RangInvalideException { if (r<1 || r>lg) throw new RangInvalideException(); if (r==lg) // supprimer le dernier lment queue = queue==0 ? lments.length-1 : --queue; else if (r==1) // supprimer le premier lment tte = tte==lments.length-1 ? 0 : ++tte; else { // dcaler les lments for (int i=tte+r; i<lg+tte; i++) // i>0 lments[(i-1) % lments.length] = lments[i % lments.length]; queue = queue==0 ? lments.length-1 : --queue; } lg--; } public void ajouter(int r, E e) throws RangInvalideException { if (lg==lments.length) throw new ListePleineException(); if (r<1 || r>lg +1) throw new RangInvalideException(); if (r==lg+1) { // ajouter en queue lments[queue]=e; queue = queue==lments.length-1 ? 0 : ++queue; } else if (r==1) { // ajouter en tte tte = tte==0 ? lments.length-1 : --tte; lments[tte]=e; } else { // dcaler les lments for (int i=lg+tte; i >= r+tte; i--) // i > 0 lments[i % lments.length ]= lments[(i-1) % lments.length]; // tte+r-1 est lindice dinsertion lments[tte+r-1]=e;

190

Chapitre 17 Structures linaires

queue = queue==lments.length-1 ? 0 : ++queue; } lg++; }

Utilisation dune structure chane Une structure chane est une structure dynamique forme de nuds relis par des liens. Les gures 17.3 et 17.4 montrent les deux types de structures chanes que nous utiliserons pour reprsenter une liste. Dans cette gure, les nuds sont reprsents par des botes rectangulaires, et les liens par des ches.
tte tte 5 20 4 9 45

F IG . 17.3 Liste avec chanage simple.


tte queue tte queue

20

45

F IG . 17.4 Liste avec chanage double.

Avec la premire organisation, chaque lment est reli son successeur par un simple lien et laccs se fait de la gauche vers la droite partir de la tte de liste qui est une rfrence sur le premier nud. Si sa valeur est gale null, la liste est considre comme vide. Dans la seconde organisation, chaque lment est reli son prdcesseur et son successeur, permettant un parcours de la liste dans les deux sens depuis la tte ou la queue. La tte est une rfrence sur le premier nud et la queue sur le dernier. La liste est vide lorsque la tte et la queue sont toutes deux gales la valeur null. Nous reprsenterons un lien par la classe gnrique Lien qui dnit simplement un attribut suivant de type Lien pour dsigner le nud suivant. Cette classe possde une mthode qui renvoie la valeur de cet attribut et une autre qui la modie. Le constructeur par dfaut initialise lattribut suivant la valeur null.
public class Lien<T> { protected Lien<T> suivant; protected Lien<T> suivant() { return suivant; } protected void suivant(Lien<T> s) { suivant=s; } } // fin classe Lien

Les nuds sont reprsents par la classe gnrique Noeud qui hrite de la classe Lien et ltend par lajout de lattribut valeur qui dsigne la valeur du nud, i.e. la valeur dun lment de la squence.

17.1

Les listes

191

Cette classe possde deux constructeurs qui initialisent un nud avec la valeur dun lment particulier, et avec celle dun lien sur un autre nud. La mthode valeur renvoie la valeur du nud, alors que la mthode changerValeur change sa valeur. La mthode noeudSuivant renvoie ou modie le nud suivant.
public class Noeud<E> extends Lien<E> { protected E valeur; public Noeud(E e) { valeur=e; } public Noeud(E e, Noeud<E> s) { valeur=e; suivant(s); } public E valeur() { return valeur; } public void changerValeur(E e) { valeur=e; } public Noeud<E> noeudSuivant() { return (Noeud<E>) suivant(); } public void noeudSuivant(Noeud<E> s) { suivant(s); } }

Avec cette structure chane, les oprations ime, supprimer, et ajouter ncessitent toutes un parcours squentiel de la liste et possdent donc une complexit gale O(n). On atteint le nud de rang r en appliquant r-1 fois lopration noeudSuivant partir de la tte de liste. Nous noterons ce nud noeudSuivantr1 (tte). Lalgorithme de lopration ime scrit :
Algorithme ime(r) {Antcdent : 1 r longueur(l)} rendre noeudSuivantr1 (tte).valeur()

Comme le montre la gure 17.5, la suppression dun lment de rang r consiste affecter au lien qui le dsigne la valeur de son lien suivant. La che en pointill reprsente le lien avant la suppression. Notez que si r est gal un, il faut modier la tte de liste.
noeud de rangr suivant

suivant
F IG . 17.5 Suppression du nud de rang r.

Algorithme supprimer(r) {Antcdent : 1 r longueur(l)} si r=1 alors tte noeudSuivant(tte) sinon noeudSuivantr2 (tte).suivant noeudSuivantr (tte) finsi lg lg-1

192

Chapitre 17 Structures linaires

Lajout dun lment e au rang r consiste crer un nouveau nud n initialis la valeur e, puis relier le nud de rang r-1 n, et enn relier le nud n au nud de rang r. Si llment est ajout en queue de liste, son suivant est la valeur null. Comme prcdemment, si r=1, il faut modier la tte de liste.
noeud de rang r

suivant suivant n

F IG . 17.6 Ajout dun nud au rang r.

Algorithme ajouter(r, e) {Antcdent : 1 r longueur(l)+1} n crer Noeud(e) noeudSuivantr2 (tte).suivant n n.suivant noeudSuivantr1 (tte) lg lg+1

Nous pouvons donner lcriture complte de la classe ListeChane.


public class ListeChane<E> implements Liste<E> { protected int lg; protected Noeud<E> tte; public int longueur() { return lg; } public E ime(int r) throws RangInvalideException { if (r<1 || r>lg) throw new RangInvalideException(); Noeud<E> n=tte; for (int i=1; i<r; i++) n=n.noeudSuivant(); // n dsigne le nud de rang r return n.valeur(); } public void supprimer(int r) throws RangInvalideException { if (r<1 || r>lg) throw new RangInvalideException(); if (r==1) // suppression en tte de liste tte=tte.noeudSuivant(); else { // cas gnral, r>1 Noeud<E> p=null, q=tte; for (int i=1; i<r; i++) { p=q; q=q.noeudSuivant(); } // q dsigne llment de rang r et p son prdcesseur

17.1

Les listes

193

p.noeudSuivant(q.noeudSuivant()); } lg--; } public void ajouter(int r, E e) throws RangInvalideException { if (r<1 || r>lg +1) throw new RangInvalideException(); Noeud<E> n=new Noeud<E>(e); if (r==1) { // insertion en tte de liste n.noeudSuivant(tte); tte=n; } else { // cas gnral, r>1 Noeud<E> p=null, q=tte; for (int i=1; i<r; i++) { p=q; q=q.noeudSuivant(); } // q dsigne llment de rang r et p son prdcesseur p.noeudSsuivant(n); n.noeudSuivant(q); } lg++; } } // fin classe ListeChane

La structure qui chane les nuds avec un simple lien ne permet quun parcours unidirectionnel de la liste. Il est pourtant utile dans certains cas dautoriser un accs bidirectionnel, en particulier pour supprimer le dernier llment de la structure. Le double chanage est dni par la classe gnrique LienDouble qui tend la classe Lien en lui ajoutant un second lien, lattribut prcdent, dont la dnition est semblable lattribut suivant. La dclaration de cette classe est la suivante :
public class LienDouble<T> extends Lien<T> { protected Lien<T> prcdent; protected Lien<T> prcdent() { return prcdent; } protected void prcdent(Lien<T> s) { prcdent=s; } } // fin classe LienDouble

Chaque nud dune structure doublement chane est reprsent par la classe gnrique
Noeud2 suivante qui tend la classe LienDouble.
public class Noeud2<E> extends LienDouble<E> { protected E valeur; public Noeud2(E e) { valeur=e; } public Noeud2(E e, Noeud2<E> p, Noeud2<E> s) { valeur=e; prcdent(p); suivant(s); } public E valeur() { return valeur; } public void changerValeur(E e) { valeur=e; }

194

Chapitre 17 Structures linaires

public public public public }

Noeud2<E> noeudSuivant() { return (Noeud2<E>) suivant(); } void noeudSuivant(Noeud2<E> s) { suivant(s); } Noeud2<E> noeudPrcdent() {return (Noeud2<E>) prcdent(); } void noeudPrcdent(Noeud2<E> s) { prcdent(s); }

Une liste doublement chane possde une tte qui dsigne le premier lment de la liste, et une queue qui indique le dernier lment. Lopration ime est identique celle qui utilise une liste simplement chane. Sa complexit est O(n). La suppression dun lment de rang r ncessite de mettre jour le lien prcdent du nud de rang r+1 sil existe (voir la gure 17.7). La complexit est O(n). Notez que la suppression du dernier lment de la liste est une simple mise jour de lattribut queue et sa complexit est O(1).
suivant noeud de rang r suivant

prcdent
F IG . 17.7 Suppression du nud de rang r.

Lajout dun lment est semblable celui dans une liste simplement chane. Mais l aussi, il faut mettre jour le lien prcdent comme le montre la gure 17.8.
noeud de rang r suivant

suivant prcdent

n
F IG . 17.8 Ajout du nud de rang r.

La classe suivante donne lcriture complte de limplantation dune liste avec une structure doublement chane.

17.1

Les listes

195

public class ListeChaneDouble<E> implements Liste<E> { protected int lg; protected Noeud2<E> tte, queue; public int longueur() { return lg; } public E ime(int r) throws RangInvalideException { if (r<1 || r> lg) throw new RangInvalideException(); Noeud2<E> n=tte; for (int i=1; i< r; i++) n=n.noeudSuivant(); // n dsigne le noeud2 de rang r return n.valeur(); } public void supprimer(int r) throws RangInvalideException { if (r<1 || r>lg) throw new RangInvalideException(); if (lg==1) // un seul lment r=1 tte=queue=null; else // au moins 2 lments if (r==1) // suppression en tte de liste tte=tte.noeudSuivant(); else if (r==lg) { // suppression du dernier lment de la liste queue=queue.noeudPrcdent(); queue.noeudSuivant(null); } else { // cas gnral, r > 1 et r < lg Noeud2<E> q=tte, p=null; for (int i=1; i<r; i++) { p=q; q=q.noeudSuivant(); } // q dsigne llment de rang r q.noeudSuivant().noeudPrcdent(p); p.noeudSuivant(q.noeudSuivant()); } lg--; } public void ajouter(int r, E e) throws RangInvalideException { if (r<1 || r>lg +1) throw new RangInvalideException(); Noeud2<E> n=new Noeud2<E>(e); if (lg==0) // liste vide r==1 tte=queue=n; else if (r==1) { // insertion en tte de liste tte.noeudPrcdent(n); n.noeudSuivant(tte); tte=n; } else if (r==lg+1) {

196

Chapitre 17 Structures linaires

// ajout du dernier lment de la liste queue.noeudSuivant(n); n.noeudPrcdent(queue); queue=n; } else { // cas gnral, r>1 et r lg Noeud2<E> p=null, q=tte; for (int i=1; i<r; i++) { p=q; q=q.noeudSuivant(); } // q dsigne llment de rang r // et p son prdcesseur p.noeudSuivant(n); n.noeudPrcdent(p); n.noeudSuivant(q); q.noeudPrcdent(n); } lg++; } } // fin classe ListeChaneDouble

17.1.3

numration

Il arrive frquemment que lon ait parcourir une liste pour appliquer un traitement spcique chacun de ses lments ; par exemple, pour afcher tous les lments de la liste, ou encore lever au carr chaque entier dune liste. Lalgorithme de parcours de la liste scrit de la faon suivante :
Algorithme parcours(l, traiter) pourtout i de 1 longueur(l) faire traiter(ime(l,i)) finpour

Il est vident que la complexit de cet algorithme doit tre O(n). Cest le cas, si la liste est implante avec un tableau. Mais, elle est malheureusement gale O(n2 ) si la liste utilise une structure chane, puisque lopration ime repart chaque fois du dbut de la liste. Une des proprits fondamentales des structures linaires est que chaque lment, hormis le dernier, possde un successeur. Il est alors possible dnumrer tous les lments dune liste grce une fonction succ dont la signature est dnie comme suit : succ : E E

Pour une liste l, cette fonction est dnie par laxiome suivant : r [1, longueur(l)[, succ(i`me(l, r)) = i`me(l, r + 1) e e Notez que la liste nest pas le seul type abstrait dont on peut numrer les composants. Nous verrons que nous pourrons numrer les lments des types abstraits que nous tudierons par la suite en fonction de leurs particularits.

17.1

Les listes

197

Limplantation en J AVA Nous reprsenterons une numration par linterface JAVA Iterator du paquetage java.util. Cette interface possde des mthodes de manipulation de lnumration. En particulier, la mthode next qui renvoie llment suivant de lnumration (ou lexception NoSuchElementException si cet lment nexiste pas) et la mthode HasNext indique si la n de lnumration a t atteinte ou pas. Linterface dnit aussi la mthode remove, mais nous ne la traiterons pas par la suite. Une liste dnira la mthode iterator qui renvoie lnumration de ses lments. Cette mthode scrit de la faon suivante :
public Iterator<E> iterator() { return new Listenumration(); }

La programmation de la classe Listenumration dpend de limplantation de la liste. Chaque classe qui implante le type abstrait Liste dnira une classe prive locale Listenumration qui donne une implantation particulire de lnumration. Par exemple dans la classe ListeTableau, on dnira :
private class Listenumration implements Iterator<E> { private int courant; private Listenumration() { courant=0; } public boolean hasNext() { return courant!=lg; } public T next() throws NoSuchElementException { if (hasNext()) return lments[courant++]; // pas de suivant throw new NoSuchElementException(); } } // Listenumration

Lattribut courant dsigne lindice du prochain lment de lnumration dans le tableau. Sa valeur initiale est gale zro. Pour la classe ListeTableauCirculaire, le calcul de llment suivant se fait modulo la longueur du tableau.
private class Listenumration implements Iterator<E> { private int courant, nbnum; private Listenumration() { courant = tte; nbnum = 0; } public boolean hasNext() { return nbnum != lg; } public E next() throws NoSuchElementException {

198

Chapitre 17 Structures linaires

if (hasNext()) { T e = lments[courant]; courant = courant == lments.length - 1 ? 0 : ++courant; nbnum++; return e; } // pas de suivant throw new NoSuchElementException(); } } // Listenumration

Lattribut nbnum est ncessaire pour identier la n de la liste, car si les indices de tte et de queue sont gaux, la liste peut tre aussi bien vide que pleine. Pour les classes qui implantent les listes laide de structures chanes, il suft de conserver une rfrence sur llment courant. Laccs au successeur se fait laide de la mthode noeudSuivant. La premire fois, sa valeur est gale la tte de liste.
private class Listenumration implements Iterator<E> { private Noeud<E> courant; private Listenumration() { courant = tte; } public boolean hasNext() { return courant!=null; } public E next() throws NoSuchElementException { if (hasNext()) { E e = courant.valeur(); courant = courant.noeudSuivant(); return e; } // pas de suivant throw new NoSuchElementException(); } } // Listenumration

Lalgorithme de parcours donn plus haut scrit en JAVA de la faon suivante. On cre dabord lnumration, puis on traite les lments un un grce la fonction next.
public void <E> parcours(Liste<E> l) { Iterator<E> num=l.iterator(); while (num.hasNext()) traiter(num.next()); }

La manipulation dune numration sapplique en gnral tous ses lments. Il peut tre en particulier trs risqu de modier la structure de donnes (e.g. la liste) au cours dun parcours dans la mesure o cela peut corrompre lnumration. Une autre faon de traiter tous les lments dune liste est dutiliser un nonc adapt, sil existe dans le langage de programmation. JAVA propose lnonc foreach avec lequel la mthode de parcours prcdent se rcrit simplement :

17.2

Les piles

199

public void <E> parcours(Liste<E> l) { for (E x : l) traiter(x); }

Notez toutefois que pour que lnonc foreach puisse sappliquer un objet de type Liste (o tout autre type dobjet), il faut que celui-ci implmente linterface Collection du paquetage java.util, et en particulier la mthode iterator voque prcdemment.

17.2

LES PILES

Une pile est une squence dlments accessibles par une seule extrmit appele sommet. Toutes les oprations dnies sur les piles sappliquent cette extrmit. Llment situ au sommet sappelle le sommet de pile. La squence forme de quatre entiers < 5 13 23 100 > est reprsente sous forme de pile par la gure 17.9.

100 23 13 5

sommet de pile

F IG . 17.9 Une pile de quatre entiers.

Lajout et la suppression dlments en sommet de pile suivent le modle dernier entr premier sorti (LIFO1 ). Les piles sont des structures fondamentales, et leur emploi dans les programmes informatiques est trs frquent. Nous avons dj vu que le mcanisme dappel des sous-programmes suit ce modle de pile. Les logiciels qui proposent une fonction undo servent galement dune pile pour dfaire, en ordre inverse, les dernires actions effectues par lutilisateur. Les piles sont galement ncessaires pour valuer des expressions postxes.

17.2.1

Dnition abstraite

Ensembles Pile est lensemble des piles dont les lments appartiennent un ensemble E quelconque. Les oprations sur les piles seront les mmes quelle que soit la nature des lments manipuls. La constante pilevide reprsente une pile vide. Pile utilise E et boolen pilevide Pile
1 Last-In

First-Out.

200

Chapitre 17 Structures linaires

Description fonctionnelle Quatre oprations abstraites sont dnies sur le type Pile : empiler dpiler e sommet est-vide? : : : : Pile E Pile Pile Pile Pile Pile E boolen

Le rle de lopration empiler est dajouter un lment en sommet de pile, celui de dpiler de supprimer le sommet de pile et celui de sommet de renvoyer llment en sommet de pile. Enn, lopration est-vide ? indique si une pile est vide ou pas. Description axiomatique La smantique des fonctions prcdentes est dnie formellement par les axiomes suivants : p Pile, e E (1) est-vide?(pilevide) = vrai (2) est-vide?(empiler(p, e)) = faux (3) dpiler(empiler(p, e)) = p e (4) sommet(empiler(p, e)) = e (5) p, p = dpiler(pilevide) e (6) e, e = sommet(pilevide) Notez que ce sont les axiomes (3) et (4) qui dnissent le comportement LIFO de la pile. Les oprations dpiler et sommet sont des fonctions partielles, et les axiomes (5) et (6) prcisent leur domaine de dnition ; ces deux oprations ne sont pas dnies sur une pile vide.

17.2.2

Limplantation en Java

La dnition fonctionnelle du type abstrait Pile est traduite par linterface suivante :
public interface Pile<E> { public boolean estVide(); public E sommet() throws PileVideException; public void dpiler() throws PileVideException; public void empiler(E e); }

Si elles oprent sur une pile vide, les mthodes dpiler et sommet mettent lexception PileVideException. Cette exception est simplement dnie par la dclaration :
public class PileVideException extends RuntimeException { public PileVideException() { super(); } }

17.2

Les piles

201

Les piles sont des listes particulires qui se distinguent par les mthodes daccs aux lments. Il est alors naturel de rutiliser, par hritage, les classes qui implmentent linterface Liste. Par la suite, et pour des raisons de lisibilit, nous considrerons que les classes dimplantation des listes dnissent les mthodes suivantes :
E lmentDeTte() { return ime(1); } E lmentDeQueue() { return ime(lg); } void ajouterEnTte(E e) { ajouter(1,e); } void ajouterEnQueue(E e) { ajouter(lg+1,e); } void supprimerEnTte() { supprimer(1); } void supprimerEnQueue() { supprimer(lg); }

La complexit des oprations de pile est O(1) quelle que soit limplantation choisie, tableau ou structure chane. Limplantation doit donc assurer un accs direct au sommet de la pile. Utilisation dun tableau La gure 17.10 montre la squence < 5 13 23 100 > mmorise dans un tableau. Pour reprer tout moment le sommet de pile, il suft dun seul indice de queue.
tte lments queue lments.length1 ... ... ...

13

23

100

sommet de pile

F IG . 17.10 Une pile implante par un tableau.

Les algorithmes des oprations de pile sont trs simples. Lopration sommet consiste retourner llment de queue, alors que dpiler et empiler consistent, respectivement, supprimer et ajouter en queue. Pour implanter la classe PileTableau, une gestion simple (i.e. non circulaire) dune liste en tableau suft. Cette classe est dclare comme suit :
public class PileTableau<E> extends ListeTableau<E> implements Pile<E> { public PileTableau() { this(MAXLM); } public PileTableau(int n) { super(n); } public boolean estVide() { return longueur()==0; } public E sommet() throws PileVideException { if (estVide()) throw new PileVideException(); return lmentDeQueue(); }

202

Chapitre 17 Structures linaires

public void empiler(E e) { if (estPleine()) throw new PilePleineException(); ajouterEnQueue(e); } public void dpiler() throws PileVideException { if (estVide()) throw new PileVideException(); supprimerEnQueue(); } }

Notez lutilisation de la fonction estPleine propre limplantation avec tableau. Cette fonction est hrite de la classe listeTableau et nappartient pas linterface Liste.
protected boolean estPleine() { return lg==lments.length; }

Utilisation dune structure chane Limplantation dune pile laide dune structure chane utilise la classe ListeChane qui ne ncessite quune rfrence sur la tte de liste. La gure 17.11 montre la squence < 5 13 23 100 >.
100
sommet de pile

tte

23

13

5
null

F IG . 17.11 Une pile implante par une structure chane.

Pour garder leur complexit O(1), les oprations devront travailler sur la tte de liste laide des mthodes lmentDeTte, ajouterEnTte et supprimerEnTte.
public class PileChane<E> extends ListeChane<E> implements Pile<E> { public PileChane() { super(); } public boolean estVide() { return longueur()==0; } public E sommet() throws PileVideException { if (estVide()) throw new PileVideException(); return lmentDeTte(); } public void empiler(E e) { ajouterEnTte(e); }

17.3

Les les

203

public void dpiler() throws PileVideException { if (estVide()) throw new PileVideException(); supprimerEnTte(); } } // fin classe PileChane

17.3

LES FILES

Les les dnissent le modle premier entr premier sorti (FIFO2 ). Les lments sont insrs dans la squence par une des extrmits et en sont extraits par lautre. Ce modle correspond la le dattente que lon rencontre bien souvent face un guichet dans les bureaux de poste, ou une caisse de supermarch la veille dun week-end. tout moment, seul le premier client de la le accde au guichet ou la caisse.
entre sortie

file
F IG . 17.12 Une le.

Le modle de le est trs utilis en informatique. On le retrouve dans de nombreuses situations, comme, par exemple, dans la le dattente dun gestionnaire dimpression dun systme dexploitation.

17.3.1

Dnition abstraite

Ensembles File est lensemble des les dont les lments appartiennent lensemble E, et la constante levide reprsente une le vide. File utilise E et boolen levide File Description fonctionnelle Quatre oprations sont dnies sur le type File : enler : dler e : premier : est-vide? : File E File File File File File E boolen

Lopration enler a pour rle dajouter un lment en queue de le, et lopration dler supprime llment en tte de le. Premier retourne le premier lment de la le et est-vide ?
2 First-In

First-Out.

204

Chapitre 17 Structures linaires

indique si une le est vide ou pas. Notez que les signatures de ces oprations sont, au mot le prs, identiques celles des oprations du type abstrait Pile. Ce sont bien les axiomes qui vont diffrencier ces deux types abstraits. Description axiomatique Ce sont en particulier, les axiomes (3) et (4), dune part, et (5) et (6) dautre part, qui distinguent le comportement de la le de celui de la pile. Ils indiquent clairement quun lment est ajout par une extrmit de la le, et quil est accessible par lautre extrmit. f File, e E (1) est-vide?(levide) = vrai (2) est-vide?(enler(f, e)) = faux (3) est-vide?(f ) premier(enler(f, e)) = e (4) non est-vide?(f ) premier(enler(f, e)) = premier(f ) (5) est-vide?(f ) dler(enler(f, e)) = levide e (6) non est-vide?(f ) dler(enler(f, e)) = enler(dler(f ), e) e e (7) f, f = dler(levide) e (8) e, e = premier(levide)

17.3.2

Limplantation en Java

Linterface suivante dcrit les signatures des oprations du type File.


public interface File<E> { public boolean estVide(); public E premier() throws FileVideException; public void dfiler() throws FileVideException; public void enfiler(E e); }

Lorsquelles oprent sur des les vides, les mthodes premier et dfiler mettent lexception FileVideException. Comme pour celle de Pile, limplantation de linterface File sappuie sur les oprations proposes par le type Liste. Quelle que soit lorganisation des donnes choisie, tableau ou structure chane, les algorithmes des oprations de File sont les mmes. Lopration premier retourne llment de tte, dler le supprime, alors que lopration enler ajoute un lment en queue. Pour que ces oprations gardent une complexit gale O(1), les classes FileTableau et FileChane devront utiliser, respectivement, les classes ListeTableauCirculaire et ListeChaneDouble.
public class FileTableau<E> extends ListeTableauCirculaire<E> implements File<E> { public FileTableau() { super(); } public FileTableau(int n) { super(n); }

17.4

Les dques

205

public boolean estVide() { return longueur()==0; } public E premier() throws FileVideException { if (estVide()) throw new FileVideException(); return lmentDeTte(); } public void enfiler(E e) { if (estPleine()) throw new FilePleineException(); ajouterEnQueue(e); } public void dfiler() throws FileVideException { if (estVide()) throw new FileVideException(); supprimerEnTte(); } }

17.4

LES DQUES

Une dque3 possde la fois les proprits dune pile et dune le. On peut donc ajouter et supprimer un lment chaque extrmit de la squence. Les lments de la squence sont accessibles par les deux extrmits.
sortie dque entre
F IG . 17.13 Une dque.

entre sortie

17.4.1

Dnition abstraite

Ensembles D`que est lensemble des dques dont les lments appartiennent un ensemble E quele conque. La constante dquevide reprsente une dque vide. Lensemble Sens = {gauche, droite} est dni pour dsigner lextrmit utilise par les diffrentes oprations de manipulation de dque. D`que utilise E, Sens et boolen e d`quevide D`que e e
3 Le

mot dque vient de langlais double ended queue .

206

Chapitre 17 Structures linaires

Description fonctionnelle Quatre oprations sont dnies sur le type D`que : e endquer e ddquer e e extrmit e e est-vide? : : : : D`que E Sens e D`que Sens e D`que Sens e D`que e D`que e D`que e E boolen

Description axiomatique Les axiomes qui dcrivent le type D`que sont lunion des axiomes des types Pile et File : e d D`que, s, s1, s2 Sens et e E e (1) est-vide?(d`quevide) = vrai e (2) est-vide?(endquer(d, e, s)) = faux e (3) extrmit(endquer(d, e, s), s) = e e e e (4) est-vide?(d) extrmit(endquer(d, e, s1), s2) = e e e e (5) non est-vide?(d) extrmit(endquer(d, e, s1), s2) = extrmit(d, s2) e e e e e (6) ddquer(endquer(d, e, s), s) = e e e e (7) est-vide?(d) ddquer(endquer(d, e, s), s) = d e e e (8) non est-vide?(d) ddquer(endquer(d, e, s1), s2) = endquer(ddquer(d, s2), t, s1) e e e e e e (9) d, d = ddquer(d`quevide, s) e e e (10) e, e = extrmit(d`quevide, s) e e e

17.4.2

Limplantation en Java

Linterface Dque suivante dcrit les signatures des oprations du type abstrait D`que. Le e type Sens est dni laide dun type numr.
public interface Dque<E> { public enum Sens { gauche, droite } public boolean estVide(); public E extrmit(int sens) throws DqueVideException; public void ddquer(int sens) throws DqueVideException; public void endquer(E e, int sens); }

Leurs algorithmes de mise en uvre du type Dque sont trs simples et leur programmation utilise les oprations du type Liste. Pour que la complexit des oprations soit O(1), limplantation de linterface Dque devra choisir un tableau gr de faon circulaire ou une structure doublement chane.
public class DqueChane<E> extends ListeChaneDouble<E> implements Dque<E> { public DqueChane() { super(); }

17.5

Exercices

207

public boolean estVide() { return longueur()==0; } public void ddquer(Sens s) throws DqueVideException { if (estVide()) throw new DqueVideException(); if (s == Sens.gauche) supprimerEnQueue(); else // s=Sens.droite supprimerEnTte(); } public void endquer(E e, Sens s) { if (s == Sens.gauche) ajouterEnQueue(e); else // s=Sens.droite ajouterEnTte(e); } public E extrmit(Sens s) throws DqueVideException { if (estVide()) throw new DqueVideException(); return s == Sens.gauche ? lmentDeQueue() : lmentDeTte(); } }

17.5

EXERCICES

Exercice 17.1. On veut enrichir le type abstrait Liste avec les oprations concatner et inverser. Leurs signatures sont les suivantes : concatner inverser : : Liste Liste Liste Liste Liste

Donnez les axiomes qui dnissent la smantique de ces oprations. crivez les mthodes
concatner et inverser qui respectent les dnitions fonctionnelles et axiomatiques pr-

cdentes. Exercice 17.2. Vous avez remarqu que limplantation des oprations ajouter et enlever avec une structure simplement chane doit tenir compte du cas particulier de la modication de la rfrence sur llment de tte. Par exemple, chaque ajout lopration vrie systmatiquement si llment est ajouter en tte de liste ou pas. Il est possible dviter ce test si on considre quune liste vide contient toujours un lment. Cet lment, sans valeur particulire, est appel lment bidon. Rcrivez les oprations de la classe ListeChane en grant un lment bidon. Exercice 17.3. Le problme voqu dans lexercice prcdent se pose galement avec llment de n dune liste doublement chane. Rcrivez les oprations de la classe ListeChaneDouble en grant deux lments bidons, respectivement, en tte et en queue de liste.

208

Chapitre 17 Structures linaires

Exercice 17.4. Utilisez une liste pour reprsenter un polynme une variable de degr n. Vous programmerez les oprations telles que laddition et la multiplication de deux polynmes. Exercice 17.5. On dsire valuer des expressions postxes formes doprandes entiers positifs et des quatre oprateurs +, -, * et /. On rappelle que dans la notation polonaise inverse loprateur suit ses oprandes. Par exemple, lexpression inxe suivante :
(7 + 2) * (5 - 3)

est dnote :
7 2 + 5 3 - *

Lvaluation dune expression postxe se fait trs simplement laide dune pile. Lexpression est lue de gauche droite. Chaque oprande lu est empil et chaque oprateur trouve ses deux oprandes en sommet de pile quil remplace par le rsultat de son opration. Lorsque lexpression est entirement lue, sans erreur, la pile ne contient quune seule valeur, le rsultat de lvaluation. crivez lalgorithme dvaluation dune expression postxe. Programmez en JAVA cet algorithme, en utilisant une classe dimplantation du type Pile. Les expressions sont lues sur lentre standard System.in et les rsultats sont crits sur la sortie standard System.out. Vous traiterez un oprateur supplmentaire, dnot par le symbole =, qui afche le rsultat. Les oprateurs et les oprandes sont spars par des blancs, des tabulations ou des passages la ligne. Vous pourrez utiliser la classe StreamTokenizer dnie dans le paquetage java.io. Un objet de cette classe prendra en entre un ot de caractres et rendra en sortie un ot dunits syntaxiques qui reprsente les diffrents composants (oprandes, oprateurs, sparateurs) dune expression. Exercice 17.6. En utilisant une pile, crivez un programme qui vrie si un texte lu sur lentre standard est correctement parenths. Il sagit de vrier si chaque parenthseur fermant rencontr ], ) ou } correspond son parenthseur ouvrant [, ( ou {. Le programme crit sur la sortie standard TRUE ou FALSE selon que le texte est correctement parenths ou pas. Exercice 17.7. La dnition du type abstrait Liste, donne dans ce chapitre, suit un modle itratif. Mais, il est galement possible den donner une dnition rcursive : Liste = + E Liste Elle nonce quune liste est soit vide, soit forme dun lment suivi dune liste. On dnit les oprations suivantes : tte : n : cons : est-vide ? : Liste Liste E Liste Liste E Liste Liste boolen

Lopration tte retourne le premier lment de la liste, et n la liste ampute du premier lment. Lopration cons construit une liste partir dun lment ( placer en tte) et dune liste.

17.5

Exercices

209

Les algorithmes de manipulation de cette forme de liste sont naturellement rcursifs. Par exemple, parcourir une liste pour appliquer un traitement sur chacun des lments scrit :
Algorithme appliquer(l, traiter) si non est-vide?(l) alors traiter(tte(l)) appliquer(fin(l)) finsi

crivez les axiomes qui dnissent la smantique des oprations prcdentes. Proposez une implantation en JAVA de ce modle de Liste laide dune structure chane. Exercice 17.8. Une matrice creuse est une matrice dont la majorit des lments sont gaux zro. Proposez une reprsentation dune matrice creuse laide dune structure chane qui ne mmorise que les valeurs diffrentes de zro.

Chapitre 18

Graphes

Un graphe est un ensemble de sommets relis par des arcs. La gure 18.1 montre un graphe particulier neuf sommets et dix arcs. Par dnition, un graphe est orient, cest-dire que les relations tablies entre les sommets ne sont pas symtriques. Toutefois, certains problmes, qui ne tiennent pas compte de lorientation, pourront les considrer comme symtriques. On parle alors de graphe non orient et les arcs qui relient les sommets sont nomms artes.
s1 s9

s2 s4

s3

s5 s6

s7

s8

F IG . 18.1 Un graphe neuf sommets et deux composantes connexes.

Les graphes interviennent dans des domaines varis tant thoriques (e.g. mathmatiques discrtes, combinatoire) que pratiques (e.g. applications informatiques), et sont trs utiliss ds quil sagit de simuler des relations complexes entre des lments dun ensemble.

212

Chapitre 18 Graphes

Les graphes servent par exemple en sociologie modliser les relations entre des individus ou en sciences conomiques. Naturellement, ce sont les outils de prdilection pour la reprsentation des rseaux (routiers, de tlcommunication, dinterconnexion de rseaux, de processeurs, etc.). Par exemple, le rseau routier entre les grandes villes dun pays peut tre assimil un graphe non orient. Un sommet reprsentera une ville et une arte une route entre deux villes. En informatique, les objets allous dynamiquement dans la zone de tas de la mmoire dun ordinateur sorganisent en graphe, et sont grs automatiquement par les rcuprateurs de mmoire1 de certains langages de programmation, comme cest le cas en JAVA. La phase doptimisation globale des compilateurs construit un graphe de ot de contrle du programme partir duquel elle amliorera le code cible produire selon des techniques classiques de substitution dappels de procdures, de rduction de puissance, etc. [ASU89]. Il est possible dajouter des informations sur les sommets pour les identier, ou sur les arcs pour les pondrer. Les noms des villes seront associs aux sommets du graphe qui modlise le rseau routier, et on placera sur chaque arte la distance qui spare deux villes. Avec ces informations, il sera possible, par exemple, de calculer le trajet le plus court pour se rendre dune ville une autre. Un graphe dont les arcs (ou les artes) portent des valuations est appel graphe valu. Il est impossible en quelques lignes introductives de prsenter tous les domaines dapplications des graphes. Nous convions le lecteur intress se reporter aux ouvrages suivants [Ber70, MB86, Gon95]. Dans ce chapitre, aprs avoir introduit quelques termes spciques aux graphes, nous prsenterons un type abstrait Graphe et ses mises en uvre possibles. Quelques algorithmes classiques sur les graphes seront donns au chapitre 23.

18.1

TERMINOLOGIE

Un graphe G = (X, U ) est form dun ensemble de sommets X et dun ensemble darcs U . Lordre dun graphe est son nombre de sommets. Les graphes creux ont peu darcs et ceux qui en possdent beaucoup sont dits denses. Un arc u = (x, y) U possde une extrmit initiale x et une extrmit nale y. x est le prdcesseur de y et y est le successeur de x. Si x = y, larc est appel une boucle. Une arte entre deux sommets est note e = [x, y]. Lensemble des voisins dun arc est lunion de lensemble de ses prdcesseurs et de ses successeurs. Un multigraphe est un graphe qui possde des boucles ou des arcs multiples (plusieurs arcs qui possdent la mme extrmit initiale et la mme extrmit nale). Un graphe simple est un graphe sans boucle, ni arc multiple. Par la suite, nous ne considrerons que les graphes simples. Le nombre darcs dun graphe simple n sommets est compris entre 0 et n(n 1), et 1 n(n 1) si on ne considre pas lorientation des arcs. 2 Deux arcs sont dits adjacents sils ont au moins une extrmit commune. Un arc u est incident un sommet x vers lextrieur si x est lextrmit initiale de u. Le demi-degr
1 En

anglais garbage-collector.

18.2

Dnition abstraite dun graphe

213

extrieur, not d+ (x), est le nombre darcs incidents vers lextrieur un sommet x. De mme, un arc u est incident un sommet y vers lintrieur si y est lextrmit nale de u. Le demi-degr intrieur, not d (x), est le nombre darcs incidents vers lintrieur un sommet x. Le degr dun sommet x est gal d (x) + d+ (x). Un graphe est complet sil existe un arc (x, y) pour tout x et y de X. Un sous-graphe G = (A, U ) dun graphe G = (X, U ) est un graphe dont les arcs de U ont leurs extrmits dans A. Un graphe partiel G = (X, V ) dun graphe G = (X, U ) est un graphe dont les sommets de X sont les extrmits des arcs de V . Un chemin est une liste de sommets dans lequel deux sommets successifs quelconques sont relis par un arc. La longueur dun chemin est gale au nombre darcs. Un chemin qui ne rencontre pas deux fois le mme sommet est dit lmentaire. Un cycle est un chemin dont le premier et le dernier sommet sont identiques. Un graphe est dit connexe sil existe un chemin reliant toute paire de sommets. Il est fortement connexe si un tel chemin existe de x vers y et de y vers x. Un graphe non connexe peut tre form de composantes (fortement) connexes. La racine dun graphe est un sommet r tel que pour tout sommet y, il existe un chemin entre r et y.

18.2

DFINITION ABSTRAITE DUN GRAPHE

Ensembles Graphe utilise Sommet, boolen graphevide Graphe avec Graphe, lensemble des graphes orients, et Sommet lensemble des sommets du graphe. La constante graphevide dnit un graphe sans sommet. Description fonctionnelle partir des dnitions donnes la section 18.1, nous pouvons proposer les oprations suivantes : ordre arc d+ ddegr e i`meSucc e ajouterArc supprimerArc ajouterSommet enleverSommet : : : : : : : : : : Graphe Graphe Sommet Graphe Sommet Graphe Sommet Graphe Sommet Graphe Sommet Graphe Sommet Graphe Sommet Graphe Sommet Graphe Sommet Sommet naturel boolen naturel naturel naturel Sommet Graphe Graphe Graphe Graphe

naturel Sommet Sommet

Lopration arc teste sil existe un arc entre deux sommets. Les oprations d + et d dnissent, respectivement, le demi-degr extrieur et le demi-degr intrieur. La fonction imeSucc renvoie le ime successeur dun sommet.

214

Chapitre 18 Graphes

Lorsquon considre un graphe non orient, on ajoute au type abstrait les oprations suivantes : arte e ajouterArte e supprimerArte e : Sommet Sommet : Graphe Sommet Sommet : Graphe Sommet Sommet boolen Graphe Graphe

Enn, pour les graphes valus, les oprations ajouterArc et ajouterArte possdent les signatures suivantes : ajouterArc ajouterArte e : Graphe Sommet Sommet E : Graphe Sommet Sommet E Graphe Graphe

avec E dsignant un ensemble de valeurs quelconques. La valeur dun arc ou dune arte est obtenue grce aux oprations suivantes : valeurArc valeurArte e : Graphe Sommet Sommet : Graphe Sommet Sommet E E

Description axiomatique g Graphe, x, y Sommet (1) x g g , g = ajouterSommet(g, x)) (2) ordre(graphevide) = 0 (3) ordre(ajouterSommet(g, x)) = ordre(g) + 1 et degr(x) = d+ (x) = d- (x) = 0 e (4) x g g , g = enleverSommet(g, x)) (5) ordre(enleverSommet(g, x)) = ordre(g) 1 et arc(y, x) d+ (y) = d+ (y) 1 (6) degr(x) = d+ (x) + d- (x) e (7) arc(x, y) g , g = ajouterArc(g, x, y)) (8) ajouterArc(g, x, y) d+ (x) = d+ (x) + 1 et d- (y) = d- (y) + 1 (9) non arc(x, y) g , g = supprimerArc(g, x, y)) (10) supprimerArc(g, x, y) d+ (x) = d+ (x) 1 et d- (y) = d- (y) 1 (11) i [1, d+ (g, x)], arc(g, x, i`meSucc(g, x, i)) = vrai e (12) i [1, d+ (g, x)], y = i`meSucc(g, x, i) non arc(g, x, y) e Lorsque lorientation des arcs ne joue aucun rle, on considre les oprations sur les artes. (13) ajouterArte(g, x, y) ajouterArc(x, y) et ajouterArc(y, x) e (14) arte(g, x, y) arc(x, y) et arc(y, x) e

18.3

LIMPLANTATION EN JAVA

Linterface gnrique Graphe suivante donne les signatures des oprations du type abstrait Graphe. Le paramtre S de cette interface reprsente les valeurs possibles de sommets du graphe.

18.3

Limplantation en Java

215

public interface Graphe<S> { public int ordre(); public boolean arte(S s1, S s2); public boolean arc(S s1, S s2); public int demiDegrInt(S s); public int demiDegrExt(S s); public int degr(S s); public S imeSucc(S s, int i); public void ajouterSommet(S s) throws SommetException; public void enleverSommet(S s) throws SommetException; public void ajouterArc(S s1, S s2) throws ArcException; public void supprimerArc(S s1, S s2) throws ArcException; public void ajouterArte(S s1, S s2) throws ArteException; public void supprimerArte(S s1, S s2) throws ArteException; public Iterator<S> iterator(); public Iterator<S> sommetsAdjacents(S s); }

Notez que linterface dnit deux oprations supplmentaires, les mthodes iterator et sommetsAdjacents. Ces mthodes renvoient lnumration, respectivement, de tous les sommets du graphe, et de tous les successeurs dun sommet pass en paramtre. Ces mthodes seront trs utiles dans la programmation des algorithmes de manipulation de graphe. En particulier sommetsAdjacents permettra un calcul de tous les successeurs bien plus efcace que :
{parcourir tous les successeurs de s dans g} pourtout i de 1 d+ (g,s) faire succ imeSucc(g,s,i) finpour

Un graphe est implant classiquement soit par une matrice dadjacence, soit par des listes dadjacence. Le choix de la reprsentation dun graphe sera guid par sa densit, mais aussi par les oprations qui sont appliques. Dune faon gnrale, plus le graphe est dense, plus la matrice dadjacence conviendra. Au contraire, pour des graphes creux, les listes dadjacences seront plus adaptes. Dans les sections suivantes, nous dcrirons ces deux sortes de mises en uvre, et les complexits des oprations seront exprimes pour un graphe dordre n.

18.3.1

Matrice dadjacence

Une matrice dadjacence nn, reprsentant un graphe n sommets, possde des lments boolens tels que m[i, j] = vrai sil existe un arc entre i et j et faux sinon. Avec cette reprsentation, la complexit en espace mmoire est O(n2 ). Le graphe de la gure 18.1 page 211 est reprsent par la matrice dadjacence suivante2 :
2 Pour

rprer facilement les arcs, les valeurs vrai sont encadres.

216

Chapitre 18 Graphes

s1 s2 s3 s1 faux vrai faux s2 faux faux faux s3 vrai faux faux s4 faux faux vrai s5 faux faux faux s6 faux faux faux s7 faux faux faux s8 faux faux faux faux faux s9 faux Une classe GrapheMatrice qui tions suivantes :

s4 s5 s6 s7 s8 s9 vrai faux faux faux faux vrai faux faux faux faux faux faux vrai faux faux faux faux faux faux faux vrai faux faux faux vrai faux faux faux faux faux faux vrai faux faux faux faux faux faux faux faux vrai faux faux faux faux faux vrai faux faux faux faux faux faux faux implante linterface Graphe peut utiliser les dclara-

protected int nbSommets; // ordre du graphe protected boolean [][] matI;

Lensemble Sommet peut tre quelconque, mais limplantation doit ncessairement offrir une bijection entre le type des indices de la matrice et le type Sommet. Ainsi, les deux fonctions suivantes doivent tre dnies : numro sommet : : Sommet int int Sommet

La fonction numro renvoie le numro de lindice dun sommet dans la matrice dadjacence, et la fonction sommet est sa rciproque. Notez que pour un graphe non orient la matrice est symtrique. Pour reprsenter un graphe valu, on choisit une matrice dont les lments reprsentent la valeur de larc entre deux sommets. Lutilisation de matrice dadjacence est commode pour tester lexistence dune arte ou dun arc entre deux sommets. La complexit de ces oprations est O(1).
public boolean arc(S s1, S s2) { return matI[numro(s1)][numro(s2)]; }

En revanche, le calcul du ime successeur dun sommet, ou celui de son demi-degr intrieur ou extrieur, ncessite n tests quel que soit le nombre de successeurs du sommet. La complexit est O(n).
public int demiDegrInt(S s) { int nbDegrsInt=0; for (int i=0; i<nbSommets; i++) if (matI[i][numro(s)]) nbDegrsInt++; return nbDegrsInt; }

18.3

Limplantation en Java

217

public int demiDegrExt(S s) { int nbDegrsExt=0; for (int i=0; i<nbSommets; i++) if (matI[numro(s)][i]) nbDegrsExt++; return nbDegrsExt; } public S imeSucc(S s, int i) { if (i<=0) throw new SommetException(); int k=0; do { if (k==nbSommets) throw new SommetException(); if (matI[s.numro()][k++]) i--; } while (i!=0); // k est le numro du ime successeur du sommet s return sommet(k-1); }

La construction des numrations de sommets suit le mme type de programmation donne la section 17.1.3. Donnons, par exemple, la mthode dnumration des successeurs dun sommet.
public Iterator<S> sommetsAdjacents(S s) { return new SommetsAdjacentsnumration(s); }

La classe SommetsAdjacentsnumration est une classe prive locale GrapheMatrice. Son constructeur fabrique une liste de successeurs, et les mthodes next et hasNext permettent son numration par rutilisation de lnumration de liste.
private class SommetsAdjacentsnumration implements Iterator<S> { private Iterator<S> numSommets; public SommetsAdjacentsnumration(S s) { // construire la liste des successeurs de s Liste<S> listeSom=new ListeChaneDouble<S>(); int i=0; do if (matI[numro(s)][i]) listeSom.ajouter(listeSom.longueur()+1,sommet(i)); while (++i<nbSommets); numSommets=listeSom.iterator(); } public boolean hasNext() { return numSommets.hasNext(); } public S next() throws NoSuchElementException { if (hasNext()) return numSommets.next(); // fin de lnumration throw new FinnumrationException(); } } // fin classe SommetsAdjacentsnumration

218

Chapitre 18 Graphes

18.3.2

Listes dadjacence

Cette reprsentation du graphe consiste associer chaque sommet la liste ordonne de ses successeurs. Ces listes sappellent des listes dadjacence. La gure 18.2 montre le graphe de la page 211 reprsent sous cette forme.

s1
s2

s2

s3
s4

s4
s3

s5
s4

s6
s5

s7
s8

s8
s8

s9

s4

s6

s9

F IG . 18.2 Le graphe de la gure 18.1 reprsent par des listes dadjacence.

En rutilisant le type Liste dni au chapitre 17, il est possible de reprsenter simplement le graphe par le tableau de listes de sommets suivant :
protected Liste<S> [] listeI;

Il est clair que cette reprsentation permet un gain de place substantiel pour des graphes creux, cest--dire lorsque le nombre darcs est petit par rapport au nombre de sommets. Un graphe orient de n sommets et p arcs ncessite n + p lments de liste, alors quun graphe non orient en ncessite n + 2p. Le calcul du demi-degr extrieur dun sommet est immdiat puisquil est gal la longueur de sa liste dadjacence. Sa complexit est O(1).
public int demiDegrExt(S s) { return listeI[numro(s)].longueur(); }

En revanche, vrier lexistence dun arc entre deux sommets ou calculer un ime successeur ncessite le parcours dune liste dadjacence. La complexit est alors gale, en moyenne, la longueur de la liste dadjacence divise par deux. La complexit dans le pire des cas est donc O(p).
public boolean arc(S s1, S s2) { return rechercher(listeI[numro(s1)], s2); } public S imeSucc(S s, int i) { return listeI[numro(s)].ime(i); }

18.4

Parcours dun graphe

219

Le calcul du demi-degr intrieur dun sommet ncessite quant lui le parcours de lensemble des listes dadjacence, cest--dire le nombre total darcs du graphe. La complexit est O(max(n, p)). Pour reprsenter un graphe valu, il suft dajouter les valuations des arcs (x, y) llment y dans la liste dadjacence du sommet x. La gure 18.3 montre cette forme de reprsentation.

s1 5 s2 6 s3

s1

s2

s3

s2 5

s3 6
F IG . 18.3 Reprsentation dun graphe valu par des listes dadjacence.

Lnumration des successeurs dun sommet s consiste simplement numrer les sommets de sa liste dadjacence.

18.4

PARCOURS DUN GRAPHE

Il existe deux types classiques de parcours de graphe : le parcours en profondeur et le parcours en largeur. Ces deux types de parcours font un parcours complet du graphe, et visitent chacun des sommets, une seule fois, pour lui appliquer un traitement.

18.4.1

Parcours en profondeur

Le parcours en profondeur, partir dun sommet s, passe dabord par ce sommet, puis consiste parcourir en profondeur chacun de ses successeurs. Ce parcours correspond donc celui dune composante connexe du graphe. Si le graphe en possde plusieurs, tous les sommets nauront pas t traits, et lalgorithme devra se poursuivre avec un parcours en profondeur dun sommet non encore trait. Le parcours peut passer plusieurs fois par un mme sommet ( cause dun cycle par exemple), et le traitement du sommet ne devra pas tre renouvel. Pour vrier si un sommet a dj t trait ou pas, on le marque aprs chaque traitement. Lalgorithme sexprime rcursivement en deux parties comme suit :

220

Chapitre 18 Graphes

Algorithme Parcours-en-Profondeur(G) {Parcours en profondeur du graphe G} pourtout s de G faire si non marqu(s) alors Pprofondeur(G, s) finsi finpour

Algorithme Pprofondeur(G, s) {Parcours en profondeur des successeurs du sommet s} mettre une marque sur s pourtout x de G tel que arc(s,x) faire si non marqu(x) alors Pprofondeur(G, x) finsi finpour

Si le traitement du sommet a lieu avant le parcours des successeurs le parcours est dit prxe. Cela correspond lexcution dune action sur le sommet courant juste avant la pose de la marque sur le sommet. En revanche, si le traitement est fait aprs le parcours des successeurs, le parcours est postxe. Laction est faite sur le sommet courant aprs lnonc itratif. Notez que deux traitements, prxe et postxe, peuvent tre appliqus lors dun mme parcours. En partant du sommet s1, les parcours en profondeur prxe et postxe du graphe de la gure 18.1 traitent les sommets selon les ordres suivants : prxe = <s1 s2 s4 s3 s6 s5 s9 s7 s8> postxe = <s2 s3 s5 s6 s4 s9 s1 s8 s7> La complexit du parcours en profondeur dpend de la reprsentation du graphe. Pour un graphe de n sommets et p arcs, la complexit de lalgorithme est O(n2 ) avec une matrice dadjacence, et O(max(n, p)) avec des listes dadjacence.

18.4.2

Parcours en largeur

On appelle distance la longueur du chemin entre deux sommets dun graphe. Le parcours en largeur dun graphe partir dun sommet origine s consiste dabord visiter ce sommet, puis traiter les sommets de distance avec s gale un, puis ceux de distance gale deux, etc. Lalgorithme scrit de faon itrative et utilise une le dattente qui conserve les sommets dj traits, par ordre de distance croissante, dont les successeurs sont visiter. Comme pour le parcours en profondeur, les nuds parcourus sont marqus an de ne pas les traiter plusieurs fois. Notez quun parcours en largeur parcourt une composante connexe du graphe. Pour un parcours complet dun graphe plusieurs composantes connexes, on dcrit lalgorithme en deux tapes comme prcdemment :

18.4

Parcours dun graphe

221

Algorithme Parcours-en-Largeur(G) {Parcours en largeur des composantes connexes du graphe G} pourtout s de G faire si non marqu(s) alors Plargeur(G, s) finsi finpour Algorithme Plargeur(G, s) {Parcours en largeur des successeurs du sommet s} mettre une marque sur s f filevide enfiler(f,s) tantque non estvide(f) faire p premier(f) dfiler(f) pourtout x de G tel que arc(p,x) faire si non marqu(x) alors mettre une marque sur x enfiler(f,x) finsi finpour fintantque

partir du sommet s1, le parcours en largeur du graphe de la gure 18.1 traite les sommets dans lordre suivant : <s1 s2 s4 s9 s3 s6 s5 s7 s8> La complexit du parcours en largeur est identique celle dun parcours en profondeur, quelle que soit la reprsentation choisie, matrice dadjacence ou listes dadjacence.

18.4.3

Programmation en Java des parcours de graphe

Les algorithmes de parcours sont implants par trois mthodes dont les signatures compltent les interfaces Graphe et GrapheValu.
public void parcoursProfondeurPrfixe(Opration<S> op); public void parcoursProfondeurPostfixe(Opration<S> op); public void parcoursLargeur(Opration<S> op);

Le traitement effectu sur chacun des sommets est contenu dans le paramtre op de type Opration. Cest une interface qui dclare lopration gnrique excuter qui prend en paramtre une donne de type gnrique sur laquelle sapplique lopration, et qui renvoie un rsultat de type Object.
public interface Opration<T> { public Object excuter(T e); }

222

Chapitre 18 Graphes

Lors dun parcours effectif dun graphe, on transmet la mthode de parcours un objet qui donne une implantation particulire de linterface Opration. Par exemple, pour afcher tous les sommets dun graphe sur la sortie standard, on pourra dnir la classe :
public class OprationAfficher<T> implements Opration<T> { public Object excuter(T e) { System.out.print(e); return null; } }

Nous donnons la programmation du parcours en largeur. Elle suit lalgorithme de la page 221, et ne pose gure de difcults. On reprsente les marques par un tableau de boolens index par le numro du sommet. Notez lutilisation de la mthode sommetsAdjacents qui renvoie lnumration des successeurs dun sommet.
private boolean[] marque; // Parcours en largeur des successeurs du sommet s // lopration op est applique sur chaque sommet private void pLargeur(S s, Opration<S> op) { File<S> f = new FileChane<S>(); marque[numro(s)]=true; f.enfiler(s); while (!f.estVide()) { S p = f.premier(); // traiter le sommet p op.excuter(p); f.dfiler(); // parcourir les successeurs de p Iterator<S> e=sommetsAdjacents(p); while (e.hasNext()) { // traiter le sommet suivant S succ=e.next(); if (!marque[numro(succ)]) { marque[numro(succ)]=true; f.enfiler(succ); } } } } // Parcours en largeur du graphe courant // lopration op est applique sur tous les sommets public void parcoursLargeur(Opration<S> op) { marque=new boolean[ordre()] for (S s : this) { if (!marque[numro(s)]) pLargeur(s,op); } } // fin parcoursLargeur

18.5

Exercices

223

Notez que lutilisation de ces mthodes de parcours na de sens que si lordre de parcours des sommets est important. Sil sagit dappliquer un traitement sur chacun des sommets du graphe dans un ordre quelconque, il conviendra dutiliser la mthode iterator qui renvoie lnumration de tous les sommets du graphe.

18.5

EXERCICES

Exercice 18.1. Dterminez le nombre maximum dartes dun graphe simple G n sommets et p composantes connexes. Exercice 18.2. Soit un graphe sept sommets donn par les listes dadjacence suivantes : s1 s3 s4 s5 s6 s2 s4 s7 s2 s4 s3 s6 s5

Dessinez le graphe que ces listes reprsentent, puis donnez la matrice dadjacence associe. Pour ce graphe particulier, quelle reprsentation vaut-il mieux choisir pour conomiser de la place mmoire ? Exercice 18.3. En partant du sommet s1 du graphe prcdent, donnez lordre de parcours des sommets pour les trois types de parcours : profondeur prxe ; profondeur postxe ; largeur. Exercice 18.4. Rdigez entirement les deux classes dimplantation de linterface Graphe (donne la page 215) laide dune matrice dadjacence et des listes dadjacence. Exercice 18.5. prcdentes. Implantez la notion de graphe valu avec les deux formes de reprsentation

Exercice 18.6. Compltez les classes prcdentes avec les mthodes de parcours en largeur et en profondeur. Exercice 18.7. crivez une fonction qui, partir de la reprsentation matricielle dun graphe, renvoie sa reprsentation sous forme de listes dadjacence. crivez sa rciproque. Exercice 18.8. On dnit lopration union qui renvoie lunion de deux graphes. Sa signature est la suivante : union : Graphe Graphe Graphe

Donnez les axiomes qui dcrivent la smantique de cette opration et programmez une mthode union pour les diffrentes reprsentations de graphe. Exercice 18.9. On appelle puits dune composante connexe, un sommet s tel que pour tout sommet x = s, il existe un arc (x, s) mais pas larc (s, x). Montrez quun graphe ne peut avoir au maximum quun seul puits et crivez la fonction chercherPuits qui recherche lexistence dun puits dans un graphe.

Chapitre 19

Structures arborescentes

Les structures arborescentes permettent de reprsenter linformation organise de faon hirarchique. Les arbres gnalogiques, les systmes de chiers de la plupart des systmes dexploitation, ou encore le texte dun algorithme ou dun programme informatique (voir la gure 19.1) sont parmi les nombreux exemples de structures hirarchiques que lon reprsente par des arbres. Lobjet de ce chapitre est ltude de la structure darbre qui est une des structures de donnes les plus importantes en informatique.

nonc conditionnel si

si

expr bool

alors

affectation

finsi

<

F IG . 19.1 Un arbre syntaxique de lnonc si x=0 alors y0 finsi.

Aprs avoir introduit la terminologie relative aux arbres, nous prsenterons dabord les arbres sous leur forme gnrale, puis nous en tudierons un forme particulire, les arbres binaires.

226

Chapitre 19 Structures arborescentes

19.1

TERMINOLOGIE

Un arbre est form dun ensemble de sommets, appels nuds, relis par des arcs et organiss de faon hirarchique. Cest en fait un graphe connexe sans cycle (voir le chapitre 18). Il existe un nud particulier appel racine qui est lorigine de larborescence. Contrairement aux arbres biologiques, les arbres informatiques poussent vers le bas, et leur racine est situe au sommet de la hirarchie. Chaque nud possde zro ou plusieurs ls, relis avec lui de faon univoque. Chaque nud, lexception de la racine, possde un pre unique. Les ls dun mme pre sont videmment des frres. Tout nud n dun arbre est accessible par un chemin unique qui part de la racine et passe par un ensemble de nuds appels ascendants de n. La racine dun arbre est donc un ascendant de tous les nuds de larbre. Rciproquement, tous les nuds accessibles par un chemin partir dun nud n sont des descendants de ce nud. La gure 19.2 montre un arbre qui possde quatorze nuds, nomms n1 jusqu n14 . Il est important de comprendre que les nuds sont distincts et portent chacun un nom unique. Dans lexemple, lordre de nomination doit tre considr comme quelconque. La racine sappelle n1 , le nud n10 est un descendant des nuds n1 et n8 , alors que n2 est lascendant des nuds n4 et n5 .

hauteur 0 n1

n2

n6

n8

2 n3

n4 n5

n7

n9 n10 n11 n12

n13

n14

F IG . 19.2 Un arbre form de quatorze nuds .

Au chapitre 15, nous avons vu comment dnir des objets rcursivement, et les arbres se prtent bien de telles dnitions. Un arbre est ainsi form dun nud racine et dune suite, ventuellement vide, darbres appels sous-arbres. Larbre de la gure 19.2 est form de la racine n1 , et de trois sous-arbres. Son premier sous-arbre a pour racine le nud n2 et deux sous-arbres, son deuxime sous-arbre a pour racine le nud n6 et un unique sous-arbre, etc. Les nuds qui ne possdent pas de sous-arbres, qui nont donc pas de ls, sont appels nuds externes ou feuilles. Ceux qui possdent au moins un sous-arbre sappellent des nuds internes. Larbre de la gure 19.2 possdent six nuds internes et huit feuilles. Les nuds n5 et n14 sont des feuilles, alors que n8 et n10 sont des nuds internes.

19.2

Les arbres

227

Une branche dun arbre est un chemin entre la racine et une feuille. Le nombre de branches dun arbre est gal son nombre de feuilles. La longueur dun chemin est dnie entre deux nuds appartenant une mme branche, et est gale au nombre darcs qui les sparent. La longueur du chemin entre les nuds n8 et n11 est gale deux, celle entre les points n2 et n4 est gale un. Notez que la longueur dun chemin dun nud unique est zro. La hauteur ou le niveau dun nud est la longueur du chemin entre la racine et lui. La hauteur ou la profondeur dun arbre est gale au maximum des hauteurs de ses nuds. La hauteur du nud n10 est gale deux, et la hauteur de larbre est trois. La profondeur moyenne dun arbre est gale la somme des hauteurs de chacun de ses nuds divise par le nombre de nuds. Un arbre tiquet est un arbre dont les nuds possdent une valeur. Dans certains arbres, seules les feuilles sont tiquetes. Le nom du nud et sa valeur sont deux notions distinctes, et plusieurs nuds peuvent possder des valeurs identiques. La gure 19.3 page 227 montre larbre prcdent tiquet avec des caractres alphabtiques.

n1 k

n2 b

n6 m

n8 k

n3

n4 e

n7 a

n9 a n10

n13 i

z n14

n5

n11 g n12 b
F IG . 19.3 Un arbre tiquet.

19.2

LES ARBRES

Un arbre possde la forme gnrale que nous avons prsente plus haut. Il est compos dun nombre ni de nuds dont le nombre de ls est quelconque. Un arbre possde toujours au moins un nud et nest donc jamais vide. Plus formellement, un arbre est dcrit par lquation rcursive suivante : Arbre For t e = Nud For t e = Arbre

Une fort est une suite quelconque darbres. Arbre dnit les suites darbres de longueur nulle, un, deux, trois, etc. et que lon note : Arbre = + < Arbre > + < Arbre Arbre > + < Arbre Arbre Arbre > . . .

228

Chapitre 19 Structures arborescentes

19.2.1

Dnition abstraite

Arbre est lensemble des arbres, Nud lensemble des nuds dun arbre, et For t lene semble des suites darbres. Une suite vide darbres est dsigne par la constante fortvide. Ensembles Arbre utilise Nud , For t e For t utilise Arbre, naturel, entier e fortvide For t e e Pour les arbres tiquets, nous ajouterons lensemble E des valeurs qui peuvent tre associes un nud. Description fonctionnelle Les oprations donnes ci-dessous permettent la construction darbre. Dautres les complteront par la suite. e cons : Nud For t racine : Arbre fort e : Arbre Arbre Nud For t e

Lopration cons construit un arbre partir dun nud et dune fort. Lopration racine renvoie le nud de racine dun arbre, et fort retourne ses ls. Si le nud est tiquet, le type abstrait est complt par les oprations suivantes : cons : Nud E valeur : Nud Nud E

La premire construit un nud tiquet, et la seconde retourne sa valeur. Enn, les oprations suivantes manipulent lensemble For t. Elles sont semblables e celles du type abstrait Liste, puisquune fort est une suite linaire darbres. longueur : For t e i`meArbre e : For t entier e ajouterArbre : For t entier Arbre e supprimerArbre : For t entier e Description axiomatique La smantique des oprations du type abstrait est donne par les axiomes suivants. Notez que les axiomes de (4) (13) sont ceux qui dnissent la construction dune liste. a Arbre, n Nud , et f For t e (1) racine(cons(n, a)) = n (2) fort(cons(n, f )) = f e (3) cons(racine(a), fort(a)) = a e (4) longueur(fortvide) = 0 e naturel Arbre For t e For t e

19.2

Les arbres

229

(5) k, 1 k longueur(f ), longueur(supprimerArbre(f, k)) = longueur(f ) 1 (6) k, 1 k longueur(f ) + 1, longueur(ajouterArbre(f, k, a)) = longueur(f ) + 1 (7) k, 1 k longueur(f ) et 1 i < k, i`meArbre(supprimerArbre(f, k), i) = i`meArbre(f, i) e e (8) k, 1 k longueur(f ) et k i longueur(f ) 1, i`meArbre(supprimerArbre(f, k), i) = i`meArbre(f, i + 1) e e (9) k, 1 k longueur(f ) + 1 et 1 i < k, i`meArbre(ajouterArbre(f, k, a), i) = i`meArbre(f, i) e e (10) k, 1 k longueur(f ) + 1 et k = i, i`meArbre(ajouterArbre(f, k, a), i) = a e (11) k, 1 k longueur(f ) + 1 et k < i longueur(f ) + 1, i`meArbre(ajouterArbre(f, k, a), i) = i`meArbre(f, i 1) e e (12) r, r < 1 et r > longueur(f ), f , f = supprimerArbre(f, r) (13) r, r < 1 et r > longueur(f ) + 1, f , f = ajouterArbre(f, r, a) Enn, si larbre est tiquet, on ajoute laxiome : (14) valeur(cons(n, e)) = e

19.2.2

Limplantation en Java

Les signatures des oprations du type abstrait Arbre sont dcrites par linterface gnrique JAVA suivante :
public interface Arbre<E> { public E racine(); public Fort<Arbre<E>> fort(); }

La classe est paramtre sur le type gnrique E des lments de larbre. Remarquez que la mthode racine renvoie un E plutt quun Noeud<E>. Pour des raisons defcacit, notre mise en uvre assimile le nud sa valeur, libre lutilisateur de crer un arbre dont les lments seront dun type Noeud particulier. Par exemple, ce dernier pourra crire la dclaration :
Arbre<Noeud<Integer>> unArbre;

avec la classe Noeud suivante :


public class Noeud<E> { private E valeur; public Noeud(E v) { valeur = v ; } public E valeur() { return valeur; } public void changerValeur(E v) { valeur = v; } }

230

Chapitre 19 Structures arborescentes

La mthode fort renvoie la fort darbres de larbre courant. Enn, lopration cons du type abstrait sera dnie par le constructeur de la classe qui implantera cette interface. Linterface gnrique suivante dnit le type For t. Notez quelle implante linterface e Collection et offrira lnumration des sous-arbres de la fort courante avec la mthode iterator.
public interface Fort<A> extends Collection<A> { public int longueur(); public A imeArbre(int r) throws RangInvalideException; public void ajouterArbre(int r, A a) throws RangInvalideException; public void supprimerArbre(int r) throws RangInvalideException; }

Dans ce qui suit, nous dcrivons deux organisations de la structure darbre. La premire utilise une structure chane, la seconde des listes dadjacence. Utilisation dune structure chane Une premire mise en uvre possible est une reprsentation chane des arbres. Chaque arbre est form dune part de sa racine et dautre part de sa fort. La classe gnrique ArbreChan qui implmente linterface Arbre est la suivante :
public class ArbreChan<E> implements Arbre<E> { protected E laRacine; protected Fort<Arbre<E>> laFort; public ArbreChan(E r, Fort<Arbre<E>> f) { laRacine = r; laFort = f; } public E racine() { return laRacine; } public Fort<Arbre<E>> fort() { return laFort; } }

La fort est dnie comme une liste darbres. La classe FortChane donne cidessous qui la dcrit hrite simplement dune implantation particulire du type abstrait Liste. Le choix de cette implantation est fonction du type darbre reprsenter. Si le nombre de ls est peu prs constant par fort, on choisira un tableau, alors que dans le cas contraire, une structure dynamique chane conviendra mieux. Cette dernire organisation est souvent appele reprsentation ls-an-ls-droit (voir la gure 19.4). Notez quelle minimise le nombre de rfrences, mais quelle perd laccs en O(1) aux ls. La dclaration suivante dnit la classe gnrique FortChane. Notez que dans cette classe lnumration des arbres de la fort sera obtenue par la mthode iterator hrite de ListeChane.

19.2

Les arbres

231

n1

n2

n6

n8

n3

n4

n7

n9

n10

n13

n14

n5

n11

n12

F IG . 19.4 Reprsentation ls-an-ls-droit de larbre de la gure 19.2.

public class FortChane<A> extends ListeChane<A> implements Fort<A> { public FortChane() { super(); } public A imeArbre(int r) throws RangInvalideException { return super.ime(r); } public void ajouterArbre(int r, A) throws RangInvalideException { super.ajouter(r,a); } public void supprimerArbre(int r) throws RangInvalideException { super.supprimer(r); } }

Utilisation des listes dadjacence Une autre faon de reprsenter les arbres est dutiliser des listes dadjacence semblables celles employes pour implanter des graphes. On construit une suite linaire de tous les nuds et on associe chacun des nuds la liste de ses ls. La gure 19.5 donne larbre de la gure 19.2 selon cette organisation. Si la suite est implante par un tableau, cette reprsentation offre un accs en O(1) chaque nud de faon indpendante de sa position dans la hirarchie, mais la gestion dynamique des ajouts et des suppressions des nuds dans larbre est plus difcile.

19.2.3

Algorithmes de parcours dun arbre

Comme pour un graphe, le parcours dun arbre passe par tous les nuds pour appliquer un traitement, toujours le mme, sur chacun dentre eux. Deux types de parcours sont possibles :

232

Chapitre 19 Structures arborescentes

n1
n2 n3 n4

n2

n3
n5

n4

n5
n7

n6

n7
n9

n8

n9
n11

n 10 n 11 n 12 n 13 n 14

n6

n10

n12

n8

n13 n14

F IG . 19.5 Arbre reprsent par des listes dadjacence.

en profondeur et en largeur. Nous prsentons lalgorithme de parcours en profondeur, celui du parcours en largeur est laiss en exercice. Le parcours en profondeur dun arbre a consiste passer par sa racine, puis parcourir en profondeur chacun de ses ls. Lalgorithme sexprime rcursivement comme suit :
Algorithme Parcours-en-Profondeur(a) {Parcours en profondeur de larbre a} pourtout fils de fort(a) faire {parcourir en profondeur le fils courant} Parcours-en-Profondeur(fils) finpour

Lors du parcours de larbre, si le traitement est appliqu sur la racine avant lnonc itratif, le parcours est prxe. Au contraire, sil a lieu aprs, le parcours est postxe. Nous donnons ci-dessous la programmation en JAVA de la mthode de parcours prxe de la classe Arbre. Le traitement de la racine est assur par une opration excuter du paramtre op de type Opration (voir la section 18.4.3, page 222). Par ailleurs, notez que lutilisation de lnonc foreach est possible car Fort est-une Collection.
public void parcoursPrfixe(Opration<E> op) { op.excuter(racine()); for (Arbre<E> a : fort()) a.parcoursPrfixe(op); }

19.3

ARBRE BINAIRE

Un arbre binaire est un arbre qui possde au plus deux ls, un sous-arbre gauche et un sous-arbre droit. Les arbres binaires sont utiliss dans de nombreuses circonstances. Ils servent, par exemple, reprsenter des gnalogies ou des expressions arithmtiques (voir la gure 19.6).

19.3

Arbre binaire

233

+ 3 a b

F IG . 19.6 Lexpression a b + 3.

Un arbre binaire nest toutefois pas un arbre dont la fort serait limite deux ls. Les deux arbres donns par la gure 19.7 sont diffrents et ne peuvent pas tre distingus avec un arbre un seul ls. Le premier possde un ls gauche et pas de ls droit. Inversement, le second possde un ls droit et pas de ls gauche.

n1

n1

n2

n2

F IG . 19.7 Deux arbres binaires distincts.

Larbre binaire (a) de la gure 19.8 possde une forme quelconque, mais certains arbres ont des formes caractristiques. On appelle arbre binaire dgnr (b), un arbre dont chaque niveau possde un seul nud (les nuds appartiennent une seule et mme branche). Un arbre binaire complet (c) est un arbre dont les nuds, qui ne sont pas des feuilles, possdent toujours deux ls. Enn, un arbre binaire parfait (d) est un arbre dont toutes les feuilles sont situes sur au plus deux niveaux ; les feuilles du dernier niveau sont places le plus gauche.

(a)

(b)

(c)

(d)

F IG . 19.8 Arbre (a) quelconque (b) dgnr (c) complet (d) parfait.

Un arbre binaire de n nuds possde une profondeur p minimale lorsquil est parfait et maximale lorsquil est dgnr. La profondeur p et le nombre de nuds n dun arbre sont tels que log2 n p n 1, o dsigne la partie entire infrieure. Cette relation est

234

Chapitre 19 Structures arborescentes

trs importante car elle dtermine la complexit de la plupart des algorithmes sur les arbres binaires. Cette complexit est comprise entre O(log2 n) et O(n). Une autre relation intressante lie le nombre de feuilles et le nombre de nuds des arbres binaires complets. Leur nombre de feuilles est gal leur nombre de nuds plus 1.

19.3.1

Dnition abstraite

Comme pour les arbres dont le nombre de sous-arbres est quelconque, nous pouvons donner une dnition rcursive dun arbre binaire. Les quations qui dcrivent un arbre binaire sont les suivantes : Arbre b Arbre b = = Nud Arbre b Arbre b

Elles signient quun arbre binaire est soit vide, soit form dun nud et de deux arbres binaires, appels respectivement sous-arbre gauche et sous-arbre droit. Notez que la notion darbre binaire vide, trangre aux arbres, a t introduite pour distinguer les deux arbres binaires de la gure 19.7 page 233. Ensembles Arbre b est lensemble des arbres binaires et possde llment particulier arbrevide qui correspond un arbre binaire vide. Les nuds de larbre appartiennent lensemble Nud . Arbre b utilise Nud et boolen arbrevide Arbre b Description fonctionnelle Les oprations suivantes sont dnies sur le type Arbre b : cons racine sag sad est-vide? : : : : : Nud Arbre b Arbre b Arbre b Arbre b Arbre b Arbre b Arbre b Nud Arbre b Arbre b boolen

Comme pour les arbres gnraux, les oprations suivantes sont dnies sur les nuds tiquets : cons : Nud E valeur : Nud Nud E

Description axiomatique Les axiomes suivants dcrivent la smantique des oprations du type abstrait Arbre b . Les deux premiers axiomes spcient un arbre vide, le troisime la faon de construire un arbre binaire, et les derniers laccs et les conditions daccs aux composants dun arbre binaire.

19.3

Arbre binaire

235

n Nud , a, g, d Arbre b (1) est-vide?(arbrevide) = vrai (2) est-vide?(cons(n, g, d)) = faux (3) cons(racine(a), sag(a), sad(a)) = a (4) racine(cons(n, g, d)) = n (5) sag(cons(n, g, d)) = g (6) sad(cons(n, g, d)) = d (7) n E, n = racine(arbrevide) (8) a Arbre b , a = sag(arbrevide) (9) a Arbre b , a = sad(arbrevide) Laxiome suivant est dni pour un arbre binaire tiquet : n Nud et e E (10) valeur(cons(n, e)) = e

19.3.2

Limplantation en Java

Linterface gnrique ArbreBinaire donne les signatures des oprations du type abstrait Arbre b . Lopration cons sera donne par le constructeur des classes qui implanteront cette interface.
public interface ArbreBinaire<E> { public E racine() throws ArbreVideException; public ArbreBinaire<E> sag() throws ArbreVideException; public ArbreBinaire<E> sad() throws ArbreVideException; public boolean estVide(); }

Comme prcdemment pour linterface Arbre, le nud est assimil sa valeur, la mthode racine renvoie un E plutt quun Noeud<E>. Les arbres binaires sont gnralement utiliss pour la mise en uvre de structures dynamiques et sont implants par des structures chanes. Toutefois, dans le cas trs particulier des arbres parfaits, on choisit souvent une implantation avec des tableaux. Structures chanes Avec cette organisation, les arbres binaires sont relis par leurs sous-arbres gauche ou droit. Un arbre binaire porte des rfrences ses deux sous-arbres, et sa racine. Un arbre vide est un arbre binaire particulier, dsign par la constante de classe
arbreVide. Ce choix, plutt que celui de la valeur null, est conditionn par la mthode estVide. Si la valeur null est utilise, un objet de type arbre binaire ne pourra

jamais tester sil est vide dans la mesure o lobjet doit exister pour que la mthode puisse tre excute ; il sera donc toujours diffrent de null. Le cot supplmentaire en espace mmoire est celui dune seule constante arbreVide pour tout arbre binaire.

236

Chapitre 19 Structures arborescentes

public class ArbreBinaireChan<E> implements ArbreBinaire<E> { public static final ArbreBinaire arbreVide = new ArbreBinaireChan(null); protected E laRacine; protected ArbreBinaire<E> sag, sad; public ArbreBinaireChan(E r, ArbreBinaire<E> g, ArbreBinaire<E> d) { laRacine = r; sag = g; sad = d; } public ArbreBinaireChan(E r) { this(r,ArbreBinaireChan.arbreVide,ArbreBinaireChan.arbreVide); } public boolean estVide() { return this == arbreVide; } public E racine() throws ArbreVideException { if (estVide()) throw new ArbreVideException(); return laRacine; } public ArbreBinaire<E> sag() throws ArbreVideException { if (estVide()) throw new ArbreVideException(); return sag; } public ArbreBinaire<E> sad() throws ArbreVideException { if (estVide()) throw new ArbreVideException(); return sad; } } // fin classe ArbreBinaireChan

Utilisation dun tableau Lutilisation dun tableau est adapte aux arbres qui voluent peu, et plus particulirement aux arbres parfaits. Considrons larbre parfait de la gure 19.9.
a

F IG . 19.9 Un arbre parfait neuf nuds .

19.3

Arbre binaire

237

Les lments de cet arbre sont rangs dans le tableau par niveau, comme le montre la gure suivante : 1 2 3 4 5 6 7 8 9
a b c d e f g h i

Avec une telle organisation, un nud dindice i, avec 1 i n div 2, possde un sousarbre gauche lindice 2i et un sous-arbre droit lindice 2i + 1. Inversement, le pre dun nud dindice i, avec 2 i n, est lindice en i div 2. Cette reprsentation peut servir pour un arbre binaire quelconque, mais convient plus particulirement aux arbres parfaits car tous les composants du tableau sont utiliss. Au contraire, cette reprsentation sera videmment exclure pour un arbre dgnr. Un tel arbre qui possde n nuds peut ncessiter un tableau de 2n 1 composants. Nous verrons la section 22.2.2 une illustration de cette reprsentation des arbres parfaits avec la mthode du tri en tas.

19.3.3

Parcours dun arbre binaire

Parcours en profondeur Le parcours en profondeur consiste passer par la racine courante, et parcourir en profondeur le sous-arbre gauche, puis le sous-arbre droit.
Algorithme Parcours-en-Profondeur(a) {Parcours en profondeur de larbre binaire a} si non estvide(a) alors Parcours-en-Profondeur(sag(a)) Parcours-en-Profondeur(sad(a)) finsi

Selon le moment o le nud courant est trait, on distingue trois types de parcours en profondeur : prxe, inxe et postxe. Le parcours prxe traite la racine en premier, puis parcourt les deux sous-arbres. Le parcours inxe parcourt le sous-arbre gauche, traite la racine, et parcourt le sous-arbre droit. Enn, le parcours postxe parcourt dabord les deux sous-arbres, et traite la racine en dernier. La programmation en JAVA du parcours inxe dun arbre binaire est donne ci-dessous. Cette mthode complte la classe ArbreBinaire. Le traitement appliquer chaque racine est donn par le paramtre op de type Opration (voir la page 222).
// Parcours en profondeur de larbre binaire courant // Lopration op est applique sur chacun de ses noeuds public void parcoursInfixe(Opration<E> op) { if (!estVide()) { sag().parcoursInfixe(op); op.excuter(racine()); sad().parcoursInfixe(op); } }

238

Chapitre 19 Structures arborescentes

Parcours en largeur Lafchage vertical dun arbre binaire sur une imprimante ou un terminal qui crit ligne par ligne impose un parcours en largeur de larbre. Comme pour lalgorithme de parcours en largeur dun graphe (voir la page 220), une le dattente est ncessaire pour conserver les nuds traits chaque niveau. Toutefois, lalgorithme de parcours de larbre est plus simple que celui du graphe, puisque larbre tant par dnition connexe et sans cycle, il est inutile de grer des marques pour sassurer quun nud na pas dj t trait.
Algorithme Parcours-en-Largeur(a) {Parcours en largeur de larbre binaire a} si non est-vide(a) alors enfiler(f, a) rpter b premier(f) dfiler(f) traiter(racine(b)) {enfiler les sous-arbres dun mme niveau} si non est-vide(sag(b)) alors enfiler(f,sag(b)) finsi si non est-vide(sad(b)) alors enfiler(f,sad(b)) finsi jusqu est-vide(f) finsi

La programmation de cet algorithme est donne par la mthode parcoursEnLargeur. Elle complte le type abstrait Arbre b .
public void parcoursEnLargeur(Opration<E> op) { if (!estVide()) { File<ArbreBinaire<E>> f = new FileChane<ArbreBinaire<E>>(); f.enfiler(this); do { ArbreBinaire<E> b = f.premier(); // traiter le nud courant op.excuter(b.racine()); f.dfiler(); // enfiler les fils sils ne sont pas vides if (!b.sag().estVide()) f.enfiler(b.sag()); if (!b.sad().estVide()) f.enfiler(b.sad()); } while (! f.estVide()); } }

19.4

Reprsentation binaire des arbres gnraux

239

19.4

REPRSENTATION BINAIRE DES ARBRES GNRAUX

Tout arbre peut tre reprsent par un arbre binaire. La reprsentation dun arbre a par un arbre binaire b est donne par les rgles de transformation suivantes : 1. racine(b) = racine(a) ; 2. tous les transforms binaires des ls de a sont lis entre eux par leur sous-arbre droit ; 3. le sous-arbre gauche de b est le transform binaire du premier ls de a. La gure 19.10 montre la transformation dun arbre qui possde une racine et une fort de k sous-arbres ( gauche) en son quivalent binaire ( droite).

n1

n1 n2 n3

n2

n3

nk

nk
F IG . 19.10 Transformation dun arbre gnral en arbre binaire.

Lalgorithme qui transforme un arbre gnral en un arbre binaire sexprime rcursivement. Les transforms binaires des sous-arbres dune fort sont lis par leur sous-arbre droit en partant du dernier sous-arbre de la fort, puis en remontant jusquau premier sous-arbre. Le sous-arbre gauche du nud courant est li au premier sous-arbre transform sous forme binaire.
Algorithme Transform-Binaire(a, b) {Rle : transforme larbre a en arbre binaire b} racine(b) racine(a) sad(b) arbrevide {lier les sous-arbres droits des transforms binaires} {des arbres de la fort de a} an arbrevide pourtout i de longueur(fort(a)) 1 faire Transform-Binaire(imeArbre(fort(a),i), frre) sad(frre) an an frre finpour sag(b) an

Nous donnons ci-dessous la programmation en JAVA de cet algorithme.

240

Chapitre 19 Structures arborescentes

public ArbreBinaire<E> transformBinaire() { ArbreBinaire<E> frre, ain = ArbreBinaireChan.arbreVide; for (int r = fort().longueur(); r >= 1; r--) { frre = fort().imeArbre(r).transformBinaire(); frre.changerSad(ain); ain = frre; } return new ArbreBinaireChan<E>(racine(), ain, ArbreBinaireChan.arbreVide); }

Notez que la mthode changerSad a t ajoute linterface ArbreBinaire. Elle permet de changer le sous-arbre droit de larbre binaire courant. Elle scrit simplement :
public void changerSad(ArbreBinaire<E> d) { sad = d; }

19.5

EXERCICES

Exercice 19.1. Donnez lalgorithme du parcours en largeur dun arbre gnral. Exercice 19.2. Montrez par rcurrence quun arbre binaire de profondeur p possde au plus 2p nuds. Exercice 19.3. Donnez les algorithmes qui calculent la hauteur dun arbre quelconque et dun arbre binaire, puis ajoutez la mthode hauteur aux classes Arbre et ArbreBinaire. Exercice 19.4. Le nombre de S TRAHLER1 est une sorte de mesure de la complexit dun arbre binaire complet. Il est dni par la fonction S suivante : si a est une feuille 0 S(sag(a)) + 1 si S(sag(a)) = S(sad(a)) S(a) = max(S(sag(a)), S(sad(a))) sinon Rdigez lalgorithme qui calcule ce nombre et ajoutez la mthode strahler la classe
ArbreBinaire.

Exercice 19.5. Deux arbres binaires a et b sont miroirs sils possdent la mme racine, si le sous-arbre gauche de a est le miroir du sous-arbre droit de b et si le sous-arbre droit de a est le miroir du sous-arbre gauche de b. crivez lalgorithme qui permet de vrier si deux arbres sont miroirs et ajoutez la mthode miroir la classe ArbreBinaire. Exercice 19.6. crivez la version itrative de lalgorithme de parcours en profondeur dun arbre binaire. Vous aurez besoin dune pile, dont la hauteur est au plus gale la profondeur de larbre.
1 Utilis lorigine en hydrographie pour dcrire la structure dun rseau de rivires, ce nombre est utilis en informatique par les compilateurs dans la gestion de lallocation des registres, ou encore par des outils graphiques de visualisation de graphe.

19.5

Exercices

241

Exercice 19.7. Il est possible de dnoter de faon univoque les nuds dun arbre binaire par des mots forms dune suite de 0 et de 1 obtenus en parcourant le chemin qui mne de la racine ce nud. Par dnition, la racine est le mot vide , et si un nud est dnot par le mot m son ls gauche est m0 et son ls droit m1. Par exemple, les noeuds de larbre donn par la gure 19.6 de la page 233 sont tels que + = , = 0, a = 00, b = 01 et 3 = 1. laide de cette notation, donnez : la reprsentation des nuds des bords gauche et droit dun arbre binaire ; la reprsentation du pre dun nud ; la hauteur dun arbre binaire. Exercice 19.8. Soit un ensemble E dlments {x1 , x2 , . . . , xn } munies de probabilits n p1 , p2 , . . . , pn , avec k=1 pk = 1. On associe lensemble E un arbre binaire construit de la faon suivante : partir des n feuilles de larbre constitues par les lments xk , on choisit les deux lments xi et xj qui possdent les probabilits les plus petites et on construit un nouveau nud x ayant xi et xj pour ls et dont la probabilit est pi +pj (on placera llment de probabilit la plus petite gauche). Dans lensemble E, on remplace xi et xj par x . Le nouvel ensemble E na plus que n 1 lments. On recommence lopration jusqu obtenir un ensemble rduit un lment. Dessinez larbre associ lensemble E = {b, e, m, x, z} muni des probabilits p(b) = 0, 21, p(e) = 0, 35, p(m) = 0, 26, p(x) = 0, 08 et p(z) = 0, 1. Pour un ensemble E de n lments, combien de nuds internes et de feuilles possdera larbre ? Rdigez lalgorithme qui construit larbre associ un ensemble E selon la mthode prcdente. Exercice 19.9. Larbre de lexercice prcdent sappelle un arbre de H UFFMAN. Associ la notation prsente lexercice no 19.7, cet arbre permet de dnir un code binaire unique pour les lments dun ensemble E. Pour lensemble E prcdent, on obtient le code : b = 11, e = 01, m = 00, x = 100 et z = 101. Avec un tel code, la suite 00011101101 se dcode sans ambigut mebez (on dcode en partant de la racine de larbre de H UFFMAN, et en suivant le chemin indiqu jusqu une feuille ; on crit la lettre correspondante et on repart de la racine pour dcoder le reste). Le codage de H UFFMAN est utilis pour comprimer des chiers de caractres. Les taux de compression peuvent varier de 30% 60% selon les chiers. Lide est de permettre un codage de longueur variable des caractres, avec le codage le plus court pour les lettres les plus frquentes. Notez que pour un ensemble dune centaine de caractres, il faut au plus log2 100 = 7 bits pour coder un caractre. crivez en JAVA une classe Huffman qui fournit deux mthodes qui, respectivement, code et dcode un chier de texte. Au pralable, vous constituerez une table des frquences des caractres partir de chiers de texte dont vous disposez. Exercice 19.10. Un arbre tiquet est reprsent sur un chier de texte sous la forme suivante : chaque nud est reprsent par son tiquette (une suite de lettres), suivie dune virgule, suivie du nombre de ls du nud (un entier naturel), suivie de la reprsentation de ses ls, spars par des virgules. Dessinez larbre dni par la suite de caractres :
pierre,3,paul,01,marie,0,claude,2,maud,00,la,1,lo,0,charles,0,

crivez lalgorithme qui construit un arbre partir de sa reprsentation textuelle lue sur un chier. Programmez cet algorithme en JAVA.

Chapitre 20

Tables

La conservation de linformation sous des formes diverses, que ce soit en mmoire centrale ou en mmoire secondaire, et la recherche dinformations partir de critres spciques est une activit trs courante en informatique. Nous appellerons table1 la structure qui permet de conserver des lments de nature quelconque, munie des oprations dajout, de suppression et de recherche. Laccs un lment se fait partir dune cl qui lidentie. Par exemple, si une table conserve des informations sur des personnes, on pourra choisir comme cl le numro I NSEE de chaque individu. Notez toutefois, que lunicit de la cl nest pas une ncessit, et que plusieurs lments distincts peuvent possder la mme cl. La faon de reprsenter une table aura une grande incidence sur la complexit des algorithmes de manipulation de table. Ces algorithmes sappuient essentiellement sur des recherches bases sur des comparaisons entre cls, et nous distinguerons par la suite les recherches positives lorsque la cl recherche est prsente dans la table et les recherches ngatives lorsquelle est absente. Les tables peuvent tre reprsentes de nombreuses faons. Dans ce chapitre, nous traiterons uniquement des tables places en mmoire centrale, et reprsentes par des listes et des arbres, ainsi que des tables dadressage dispers. Mais, tout dabord, dcrivons formellement la notion de table par son type abstrait Table.

1 Le

terme dictionnaire est galement employ pour dsigner cette structure.

244

Chapitre 20 Tables

20.1
20.1.1

DFINITION ABSTRAITE
Ensembles

Table dnit lensemble des tables qui mmorisent des lments de lensemble E, chacun des lments tant muni dune cl prise dans Cl . e Table utilise E et Cl e tablevide Table

20.1.2

Description fonctionnelle

Les signatures des trois oprations de base sur les tables sont donnes par : ajouter : Table E supprimer : Table Cl e rechercher : Table Cl e Table Table E

De plus, lopration qui permet dobtenir la cl dun lment partir de sa valeur est dnie par : cl : E e Cl e

20.1.3

Description axiomatique

Pour cette description axiomatique, nous compltons le type abstrait par lopration occurrences qui renvoie le nombre doccurrences dune cl dans une table. occurrences : Table Cl e (1) (2) (3) (4) (5) naturel

occurrences(tablevide, c) = 0 cl(e) = c occurrences(ajouter(t, e), c) = occurrences(t, c) + 1 e cl(e) = c occurrences(ajouter(t, e), c) = occurrences(t, c) e occurrences(t, c) = 0 t, t = supprimer(t, c) occurrences(t, c) 1 occurrences(supprimer(t, c), c) = occurrences(t, c) 1 (6) c = c occurrences(supprimer(t, c), c ) = occurrences(t, c ) (7) occurrences(t, c) = 0 e, e = rechercher(t, c) t (8) occurrences(t, c) 1 cl(rechercher(t, c)) = c e

20.2

REPRSENTATION DES LMENTS EN JAVA

Pour toutes les reprsentations des tables de ce chapitre, les lments sont forms dune valeur et dune cl de type quelconque. Pour les dnir, nous utiliserons la classe gnrique lment suivante :

20.2

Reprsentation des lments en Java

245

public class lment<V,C> { protected V valeur; protected C cl; public lment(V v, C c) { valeur = v ; cl = c; } public C cl() { return cl; } public V valeur() { return valeur; } public void changerCl(C c) { cl = c; } public void changerValeur(V v) { valeur = v; } }

Les cls sont des objets quelconques, mais doivent possder des oprateurs relationnels ncessaires aux oprations du type abstrait Table. chaque table, nous associerons les oprations de comparaison propres lensemble des cls utilis2 . Les signatures de ces oprations sont donnes par linterface gnrique suivante :
public interface Comparateur<T> { public boolean comparable(Object x); public boolean gal(T x, T y); public boolean infrieur(T x, T y); public boolean infrieurOugal(T x, T y); public boolean suprieur(T x, T y); public boolean suprieurOugal(T x, T y); }

La mthode comparable vrie si deux cls peuvent tre compares, cest--dire si elles sont de mme nature. Les autres mthodes se passent de commentaires. Par exemple, des cls reprsentes par des entiers seront implantes comme suit :
public class ComparateurDeClEntire implements Comparateur<Integer> { public boolean comparable(Object x) { return (x == null) ? false : Integer.class.isAssignableFrom(x.getClass()); } public boolean gal(Integer x, Integer y) { return x == y; } public boolean infrieur(Integer x, Integer y) { return x < y; } public boolean infrieurOugal(Integer x, Integer y) { return x <= y; } public boolean suprieur(Integer x, Integer y) { return x > y; }
2 Notez

quil aurait t aussi possible de munir chaque cl de ses oprations de comparaison.

246

Chapitre 20 Tables

public boolean suprieurOugal(Integer x, Integer y) { return x >= y; } } // ComparateurDeClEntire

Les axiomes 4 et 7 du type abstrait Table montrent que les oprations rechercher ou supprimer chouent si la cl nest pas prsente dans la table. Il est possible de traiter cette situation de plusieurs faons, soit en signalant une erreur, soit en renvoyant un lment spcial, ou encore en ajoutant aux oprations un boolen qui indique lchec de lopration. Par la suite, nous retiendrons la premire solution, et les oprations mettront lexception ClNonTrouveException. Les reprsentations de table que nous allons tudier maintenant, cest--dire laide de listes, darbres et de fonctions dadressage dispers, implanteront toutes linterface gnrique Table suivante :
public interface Table<V,C> { public void ajouter(lment<V,C> e); public void supprimer(C cl); public lment<V,C> rechercher(C cl) throws ClNonTrouveException; }

20.3
20.3.1

REPRSENTATION PAR UNE LISTE


Liste non ordonne

La reprsentation dune table par une liste non ordonne est la mthode la plus simple et la plus nave. Lajout des lments peut se faire nimporte o, et en particulier en tte ou en queue de liste, selon la reprsentation choisie de la liste, an de garder une complexit en O(1). Lopration de suppression ncessite une recherche de llment supprimer, suivie ou non dun dcalage dlments si la table est reprsente par un tableau. Lalgorithme de recherche consiste comparer les lments un un jusqu ce que lon ait trouv llment, ou alors atteint la n de la liste. Une liste linaire l peut tre dnie rcursivement (cf. exercice 17.6) comme tant, soit la liste vide, soit la concatnation dun lment de tte e avec une liste l que nous noterons < e, l >. La dnition axiomatique de rechercher sexprime alors comme suit : (1) e, e = rechercher(listevide, c) (2) cl(e) = c rechercher(< e, l >, c) = e e (3) cl(e) = c rechercher(< e, l >, c) = rechercher(l, c) e Lalgorithme de recherche ci-dessous parcourt la liste de faon itrative et compare la cl recherche celle de chacun des lments. La recherche sarrte lorsque la cl recherche est trouve ou lorsque la liste a t entirement parcourue. Dans ce dernier cas, la recherche est ngative.

20.3

Reprsentation par une liste

247

Algorithme rechercher(t, c) {Rle : rechercher dans la table t llment de cl c} {Consquent : renvoie llment e de cl c k, 1 k longueur(t), cl(ime(t,k))=c} i 1 tantque i=longueur(t) faire {i<longueur(t) et k, 1 k<i, cl(ime(t,k))=c} x ime(t,i) si cl(x)=c alors {cl trouve} rendre x sinon i i+1 finsi fintantque {k, 1 k longueur(t), cl(ime(t,k))=c}

Sil y a quiprobabilit dans la recherche des lments, le cot moyen dune recherche 1 positive, exprim en nombre de comparaisons, est 2 (n+1), et n pour une recherche ngative. Cet algorithme peut tre lgrement amlior grce une sentinelle. On supprime le test de n de liste en ajoutant systmatiquement la cl recherche lextrmit de la liste :
Algorithme rechercher(t, c) i 1 ajouter un lment bidon de cl c en fin de liste tantque c=ime(t,i) faire {i longueur(t)+1 et k, 1 k<i, cl(ime(t,k))=c} i i+1 fintantque {cl(ime(t,i))=c} si i longueur(t) alors rendre ime(t,i) finsi {k, 1 k longueur(t), cl(ime(t,k))=c}

La classe ListeNonOrdonneChane reprsente une liste dont les lments sont ordonns par des cls dont les oprateurs de comparaisons sont contenus dans lattribut comp de type Comparateur. Cette classe implante linterface Table. Son constructeur mmorise les oprations de comparaison du type de cl utilis.
public class ListeNonOrdonneChane<V,C> extends ListeChane<lment<V,C>> implements Table<V,C> { protected Comparateur<C> comp; public ListeNonOrdonneChane(Comparateur<C> cmp) { comp=cmp; } ... }

248

Chapitre 20 Tables

En suivant lalgorithme initial (sans sentinelle), la mthode rechercher scrit :


public lment<V,C> rechercher(C cl) throws ClNonTrouveException { if (! comp.comparable(cl)) throw new ClIncomparableException(); for (lment<V,C> x : this) if (comp.gal(x.cl(), cl)) return x; // k, 1 k longueur(this), cl(ime(this,k))=c throw new ClNonTrouveException(); }

Sil ny a pas quiprobabilit dans la recherche des lments, il est possible dordonner les lments de faon placer en tte de liste les lments les plus recherchs. Toutefois, on na pas toujours connaissance des lments les plus recherchs. Dans ce cas, il faut faire voluer la liste pour que les lments les plus recherchs soient situs en tte. Cest ce que lon appelle la recherche auto-adaptative. Citons deux algorithmes peu coteux : aprs chaque recherche, on place llment recherch en tte de liste ; cette mthode est bien adapte si la liste est reprsente sous forme chane ; aprs chaque recherche, on fait progresser llment recherch dune place vers la tte de la liste.

20.3.2

Liste ordonne

Nous considrerons que les lments dune liste ordonne l sont en ordre croissant de telle faon que : i, j [1, longueur(l)], i < j cl(i`me(l, i)) e e cl(i`me(l, j)) e e

Les axiomes qui dnissent les oprations du type abstrait Table sont exprims laide de la dnition rcursive des listes. (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) e, e = rechercher(listevide, c) cl(e) = c rechercher(< e, l >, c) = e e cl(e) < c rechercher(< e, l >, c) = rechercher(l, c) e cl(e) > c e e = rechercher(< e, l >, c) e ajouter(listevide, e) =< e, listevide > cl(e) < cl(e ) ajouter(< e, l >, e ) =< e, ajouter(l, e ) > e e cl(e) cl(e ) ajouter(< e, l >, e ) =< e , < e, l >> e e cl(e) = c supprimer(< e, l >, c) = l e cl(e) < c supprimer(< e, l >, c) =< e, supprimer(l, c) > e cl(e) > c l , l = supprimer(< e, l >, c) e

Les oprations parcourent la liste tant que la cl de llment rechercher, supprimer ou ajouter est suprieure celle de llment courant.

20.3

Reprsentation par une liste

249

Avec une liste ordonne, les trois oprations de base sont en O(n), avec une complexit moyenne gale 1 (n + 1). 2 La position dun nouvel lment ajouter suit celle de llment qui lui est immdiatement infrieur. Avec une reprsentation chane de la liste, lopration ajouter recherche la position dinsertion, puis modie le chanage, selon la mme technique que pour la liste non ordonne. Si cet lment est le plus petit, linsertion est en tte de liste. Si la liste est reprsente par un tableau, les oprations supprimer et ajouter sont plus coteuses dans la mesure o elles doivent dcaler une partie des lments du tableau. Lalgorithme de recherche parcourt la liste de faon squentielle jusqu ce que lon ait trouv un lment de cl suprieure ou gale celle recherche. En cas dgalit, llment recherch est trouv.
Algorithme rechercher(t, c) i 1 trouv faux tantque non trouv et i=longueur(t) faire {i=longueur(t) et k, 1 k<i, cl(ime(t,k))<c} x ime(t,i) si cl(x) c alors trouv vrai sinon i i+1 finsi fintantque si cl(x)=c alors rendre x finsi {k, 1 k longueur(t), cl(ime(t,k))=c}

Sil y a quiprobabilit dans la recherche des lments, le cot moyen dune recherche 1 positive et ngative est 2 (n + 1). La complexit est O(n) comme pour la liste non ordonne. La programmation en JAVA de cette mthode est donne ci-dessous :
public lment<V,C> rechercher(C cl) throws ClNonTrouveException { if (!comp.comparable(cl)) throw new ClIncomparableException(); for (lment<V,C> x : this) if (comp.suprieurOugal(x.cl(), cl)) if (comp.gal(x.cl(), cl)) // cl trouve return x; else // cl(lt courant) > cl throw new ClNonTrouveException(); // k, 1 k longueur(this), cl(ime(this,k))=c throw new ClNonTrouveException(); }

La complexit des mthodes de recherche squentielle dans des listes ordonnes ou non nest pas trs bonne puisquelle est de lordre de n. Toutefois, elles mettent en jeu des algorithmes trs simples, qui peuvent tre raisonnablement utiliss pour des tables de petite taille.

250

Chapitre 20 Tables

20.3.3

Recherche dichotomique

Le principe de lalgorithme est de diviser lespace de recherche de llment en deux espaces de mme taille. Llment recherch est dans lun des deux espaces. La recherche se poursuit dans lespace qui contient llment recherch selon la mme mthode. Cette mthode de rsolution par partition est une mthode classique qui consiste diviser un problme en sous-problmes de mme nature mais de taille infrieure. La recherche dichotomique ncessite une table ordonne et, pour tre efcace, un accs direct chaque lment de la table. La reprsentation habituelle de la table est le tableau. Au dbut, lespace de recherche est la liste entire, depuis un rang gauche gal 1 jusqu un rang droit gal la longueur de la table. Le rang du milieu (gauche + droit)/2 divise la table en deux. Si la cl recherche est gale celle de llment du milieu alors la recherche sachve avec succs, sinon, si elle lui est infrieure, la recherche se poursuit dans lespace de gauche, sinon elle lui est suprieure et la recherche lieu dans lespace de droite. La recherche choue lorsque lespace de recherche devient vide, cest--dire lorsque les rangs gauche et droit se sont croiss. Une criture vidente de cette mthode est :
Algorithme rechercher(t, c) gauche 1 droit longueur(t) rpter {gauche droit et k, 1 k<gauche, cl(ime(t,k))<c et k, droit<k longueur(t), cl(ime(t,k))>c} milieu (gauche+droit)/2 x ime(t,milieu) si c=cl(x) alors rendre x sinon si c<cl(x) alors droit milieu-1 sinon {c>cl(x)} gauche milieu+1 finsi finsi jusqu gauche>droit {k, 1 k longueur(t), cl(ime(t,k))=c}

Une autre version de cet algorithme est donne ci-dessous. Si llment est trouv, les deux bornes gauche et droit sont modies de telle faon que la boucle sachve et milieu indique alors le rang de llment recherch.
Algorithme rechercher(t, c) gauche 1 droit longueur(t) {lespace de recherche est au dpart toute la table} rpter {gauche droit et k, 1 k<gauche, cl(ime(t,k))<c et k, droit<k longueur(t), cl(ime(t,k))>c } milieu (gauche+droit)/2 x ime(t,milieu)

20.3

Reprsentation par une liste

251

si c cl(x) alors gauche milieu+1 finsi si c cl(x) alors droit milieu-1 finsi jusqu gauche>droit {cl(ime(t,milieu))=c ou k, 1 k longueur(t), cl(ime(t,k))=c} si cl(x)=c alors rendre x finsi {k, 1 k longueur(t), cl(ime(t,k))=c}

Lcriture de cet algorithme peut tre amlior en regroupant les deux tests dgalit en un seul :
Algorithme rechercher(t, c) gauche 1 droit longueur(t) rpter {gauche<droit et k, 1 k<gauche, cl(ime(t,k))<c et k, droit k longueur(t), cl(ime(t,k)) c} milieu (gauche+droit)/2 x ime(t,milieu) si c cl(x) alors droit milieu sinon {c>cl(x)} gauche milieu+1 jusqu gauche droit {cl(ime(t,gauche))=c ou k, 1 k longueur(t), cl(ime(t,k))=c} x ime(tab,gauche) si cl(x)=c alors rendre x finsi {k, 1 k longueur(t), cl(ime(t,k))=c}

Vous remarquerez que cet algorithme ne sarrte pas lorsque llment recherch est trouv ! Au contraire, il se comporte de la mme faon que sil recherchait un lment nappartenant pas la table. Si lon sait que la plupart des recherches sont ngatives, cette criture est trs efcace dans la mesure o lalgorithme ne fait plus quune seule comparaison de cl chaque itration. Dautre part, si un mme lment apparat plusieurs fois dans la table, cet algorithme permet de rechercher sa premire occurrence, cest--dire llment le plus gauche dans la table. Dans une table n lments, le nombre de comparaisons pour une recherche ngative ou positive (dans le pire des cas) est gal log2 n + 1. Cela correspond au parcours entier dune branche dun arbre binaire fortement quilibr. Le nombre moyen de comparaisons est denviron log2 n 1 pour une recherche positive, et log2 n pour une recherche ngative. Dune faon gnrale, les algorithmes de recherche dichotomique ont une complexit gale O(log2 n). Les ajouts et les suppressions dlments en table par la mthode dichotomique ne sont pas efcaces dans la mesure o ils ncessitent des dcalages traits de faon squentielle pour maintenir lordre de la table. La mthode dichotomique est donc adapte des tables qui nvoluent pas ou trs peu, comme par exemple, la table des mots rservs dun langage dans un compilateur. Lorsque les tables doivent voluer, nous prfrerons une reprsentation avec des arbres binaires.

252

Chapitre 20 Tables

20.4

REPRSENTATION PAR UN ARBRE ORDONN

La table est reprsente par un arbre binaire ordonn, cest--dire un arbre binaire tiquet dont les valeurs sont ranges suivant une relation dordre. Par la suite, nous considrerons la relation suivante entre les nuds : a Arbre bo , e sag(a), e sad(a) cl(e) cl(valeur(racine(a))) < cl(e ) e e e Toutes les cls du sous-arbre gauche sont infrieures ou gales la cl du nud, elle-mme strictement infrieure toutes les cls du sous-arbre droit. Dans le chapitre prcdent, nous avons dj voqu les arbres binaires et les algorithmes associs. Nous avons vu que pour tout arbre binaire ayant n nuds et une profondeur p, on a la double ingalit suivante : log2 n p n1

La pire des recherches dans un arbre dgnr est alors semblable celle dans une liste linaire ordonne. Alors que pour un arbre parfaitement quilibr, elle sera gale log2 n + 1. La complexit peut donc varier de O(n) O(log2 n). La profondeur moyenne dun arbre est de lordre de n. Le nombre de comparaisons moyen pour la recherche, lajout et la suppression dun lment dans un arbre binaire de recherche quelconque est environ gal 2 log2 n. Dans la programmation en JAVA des mthodes de la table, un arbre binaire ordonn sera reprsent par une structure chane dont la dnition a t donne la section 19.3.2.
public class ArbreOrdonnChan<V,C> implements Table<V,C> { protected ArbreBinaire<lment<V,C>> r; // la racine de larbre protected Comparateur<C> comp; public ArbreOrdonnChan(Comparateur<C> cmp) { r = ArbreBinaireChan.arbreVide; comp = cmp; } ... }

20.4.1

Recherche dun lment

Nous noterons a =< n, g, d > larbre a qui possde un nud n, un sous-arbre gauche g et un sous-arbre droit d. partir de cette notation, les axiomes de la recherche sexpriment comme suit : a Arbre bo , et c Cl e (1) e, e = rechercher(arbrevide, c) (2) c = cl(valeur(n)) rechercher(< n, g, d >, c) = valeur(n) e (3) c < cl(valeur(n)) rechercher(< n, g, d >, c) = rechercher(g, c) e (4) c > cl(valeur(n)) rechercher(< n, g, d >, c) = rechercher(d, c) e

20.4

Reprsentation par un arbre ordonn

253

La programmation en JAVA donne ci-dessous correspond au modle rcursif des axiomes, et est semblable celui de la recherche dichotomique. Si la cl de llment du nud courant est la cl recherche alors la recherche sachve sur un succs, sinon elle se poursuit rcursivement dans le sous-arbre gauche, si la cl recherche est infrieure celle du nud courant, ou dans le sous-arbre droit dans le cas contraire. La recherche choue lorsquun arbre vide est atteint.
public lment<V,C> rechercher(C cl) throws ClNonTrouveException { if (!comp.comparable(cl)) throw new ClIncomparableException(); return rechercher(r, cl); } private lment<V,C> rechercher(ArbreBinaire<lment<V,C>> a, C cl) throws ClNonTrouveException { if (a.estVide()) throw new ClNonTrouveException(); C clDuNoeud = a.racine().cl(); if (comp.gal(clDuNoeud, cl)) // llment recherch est trouv return a.racine(); // poursuivre la recherche return comp.suprieur(clDuNoeud, cl) ? rechercher(a.sag(), cl) : rechercher(a.sad(), cl); }

Cette programmation avec deux mthodes est ncessaire pour viter le test de compatibilit de cl chaque appel rcursif.

20.4.2

Ajout dun lment

Il existe plusieurs faons dajouter un lment dans un arbre binaire ordonn, mais toutes doivent maintenir la relation dordre. La plus simple consiste placer llment lextrmit dune des branches de larbre. Ce nouvel lment est donc une feuille. Les axiomes suivants dnissent lopration ajouter en feuille. a Arbre bo , ete E (5) ajouter(arbrevide, e) =< e, arbrevide, arbrevide > (6) cl(e) cl(valeur(n)) ajouter(< n, g, d >, e) =< n, ajouter(g, e), d > e e (7) cl(e) > cl(valeur(n) ajouter(< n, g, d >, e) =< n, g, ajouter(d, e) > e e La programmation en JAVA de ces axiomes est donne ci-dessous. Notez lutilisation des mthodes changerSag et changerSad pour changer la valeur du sous-arbre gauche ou droit dun arbre.

254

Chapitre 20 Tables

public void ajouter(lment<V,C> e) { if (! comp.comparable(e.cl())) throw new ClIncomparableException(); r = ajouter(r, e); } private ArbreBinaire<lment<V,C>> ajouter(ArbreBinaire<lment<V,C>> a, lment<V,C> e) { if (a.estVide()) return new ArbreBinaireChan<lment<V,C>>(e); // a nest pas vide if (comp.suprieurOugal(a.racine().cl(), e.cl())) // cl du nud courant e.cl // ajouter nud dans le sous-arbre gauche a.changerSag(ajouter(a.sag(), e)); else // cl du nud courant < e.cl // ajouter nud dans le sous-arbre droit a.changerSad(ajouter(a.sad(), e)); return a; }

Malheureusement, lajout en feuille a linconvnient majeur de construire des arbres dont la forme dpend des squences dajouts. Dans le pire des cas, si la suite dlments est croissante (ou dcroissante), larbre engendr est dgnr.

20.4.3

Suppression dun lment

Lopration qui consiste supprimer un lment de larbre est lgrement plus complexe. Si llment retirer est un nud qui possde au plus un sous-arbre, cela ne pose pas de difcult. En revanche, sil possde deux sous-arbres, il y a un problme : il faut lier ses deux sous-arbres un seul point ! La solution pour contourner cette difcult est de remplacer llment enlever par le plus grand lment du sous-arbre gauche (ou le plus petit lment du sous-arbre droit) et de supprimer ce dernier. Dans les deux cas, la relation dordre est conserve. a Arbre bo , e E, et c Cl e (8) a, a = supprimer(arbrevide, c) (9) c < cl(valeur(n)) supprimer(< n, g, d >, c) =< c, supprimer(g, c), d > e (10) c > cl(valeur(n) supprimer(< n, g, d >, c) =< n, g, supprimer(d, c) > e (11) c = cl(valeur(n)) supprimer(< n, g, arbrevide >, c) = g e (12) c = cl(valeur(n)) supprimer(< n, arbrevide, d >, c) = d e (13) g, d = arbrevide et c = cl(valeur(n)) e supprimer(< n, g, d >, c) =< max(g), supprimer(g, cl(max(g)), d > e avec la fonction max dnie comme suit :

20.4

Reprsentation par un arbre ordonn

255

max : Arbre bo

(14) max(< n, g, arbrevide >) = valeur(n) (15) d = arbrevide, max(< n, g, d >) = max(d) La programmation de lopration supprimer suit la dnition axiomatique prcdente.
public void supprimer(C cl) throws ClNonTrouveException { if (! comp.comparable(cl)) throw new ClIncomparableException(); r = supprimer(r, cl); } private ArbreBinaire<lment<V,C>> supprimer(ArbreBinaire<lment<V,C>> a, C cl) { if (a.estVide()) // arbre vide non trouve throw new ClNonTrouveException(); C clDuNoeud = a.racine().cl(); if (comp.infrieur(cl,clDuNoeud)) a.changerSag(supprimer(a.sag(), cl)); else if (comp.suprieur(cl,clDuNoeud)) a.changerSad(supprimer(a.sad(), cl)); else // clDuNoeud = cl est trouv if (a.sad().estVide()) // a = sag a = a.sag(); else if (a.sag().estVide()) // a = sad a = a.sad(); else // a est un nud qui possde deux fils. // Remplacer la racine du nud a par celle // du plus grand lment du sag et supprimer // ce dernier a.changerSag(suppmax(a.sag(), a)); return a; } private ArbreBinaire<lment<V,C>> suppmax(ArbreBinaire<lment<V,C>> a, ArbreBinaire<lment<V,C>> o) { assert !a.estVide(); if (!a.sad().estVide()) { a.changerSad(suppmax(a.sad(), o)); return a; } else { // racine(a) est llment max recherch o.changerRacine(a.racine()); return a.sag(); } }

256

Chapitre 20 Tables

Les oprations dajout et de suppression que vous venons de prsenter ne donnent aucune garantie sur la forme des arbres quelles retournent, et peuvent en particulier, conduire des performances en temps linaires, semblables celles des listes lorsque les arbres sont dgnrs. Pour garantir systmatiquement une complexit logarithmique, il faut adapter les mthodes dajout et de suppression dlments an quelles conservent larbre quilibr.

20.5

LES ARBRES AVL

Un arbre AVL3 est un arbre binaire quilibr tel que pour nimporte quel de ses sousarbres, appelons-le a, la diffrence de hauteur de ses sous-arbres gauche et droit nexcde pas un. Cette proprit est exprime par lingalit suivante : | hauteur(sag(a)) hauteur(sad(a))| 1.

La gure 20.1 montre un exemple darbre sept nuds qui possde la proprit AVL :

F IG . 20.1 Un arbre AVL sept nuds .

Pour un ensemble de n nuds, la hauteur dun arbre AVL est toujours O(log2 n). Plus prcisment, A DELSON-V ELSKII et L ANDIS ont montr (cits par [Wir76]) que la hauteur h dun arbre AVL qui possde n nuds est lie par la relation : log2 (n + 1) h 1, 4404 log2 (n + 2) 0, 328.

20.5.1 Rotations
Les arbres AVL servent reprsenter les arbres binaires ordonns et garantissent une complexit de recherche de lordre de O(log2 n). Les oprations dajout et de suppression de la section 20.4 peuvent crer un dsquilibre qui remet en cause la proprit des arbres AVL. La diffrence de hauteur des sous-arbres qui violent la proprit est alors gale 2. Pour
3 Les arbres AVL doivent leur nom aux initiales de leurs auteurs, A DELSON -V ELSKII et L ANDIS , deux informaticiens russes qui les inventrent en 1962.

20.5

Les arbres AVL

257

conserver la proprit AVL, ces oprations doivent rquilibrer larbre au moyen de deux types de rotation, les rotations simples et les rotations doubles. Ces rotations doivent bien sr conserver la relation dordre sur les cls. Considrons larbre (a) de la gure 20.2. Le nud dont la cl a pour valeur 1 provoque un dsquilibre gauche et rclame une restructuration de larbre, appele rotation simple gauche. Cette gure montre larbre (b) obtenu aprs cette rotation.
3 2

1 (a) (b)

F IG . 20.2 Une rotation simple gauche.

La gure 20.3 montre le cas gnral dun dsquilibre d au sous-arbre le plus gauche (a), et la rotation simple gauche qui permet de rquilibrer larbre an de retrouver la proprit AVL (b).
y x y x

(a)
F IG . 20.3 Rotation simple gauche.

(b)

De faon symtrique, un dsquilibre se produit lorsque le sous-arbre le plus droite est trop grand. Le rquilibrage de larbre est assur par une rotation simple droite, strictement symtrique la prcdente comme le montre la gure 20.4. Les deux rotations simples prcdentes ne peuvent rsoudre toutes les formes de dsquilibre. Prenons le cas de larbre (a) de la gure 20.5. Llment de cl 2 viole la proprit AVL et provoque un dsquilibre dans le sous-arbre droit du sous-arbre gauche de larbre. La rorganisation de larbre (b) est effectue laide dune rotation double gauche. Une rotation

258

Chapitre 20 Tables

x x y

(a)
F IG . 20.4 Rotation simple droite.

(b)

double gauche consiste appliquer successivement deux rotations simples : une rotation simple droite du sous-arbre gauche, suivie dune rotation simple gauche de larbre.
3 2

2 (a) (b)

F IG . 20.5 Une rotation double gauche.

La gure 20.6 montre la forme gnrale dun arbre auquel il faut appliquer une rotation double gauche pour le rquilibrer. Comme pour la rotation simple gauche, il existe une rotation double droite, symtrique de la gauche comme le montre la gure 20.7. Celle-ci consiste en une rotation simple gauche du sous-arbre droit, suivie dune rotation simple droite de larbre. Les oprations dinsertion et de suppression nont besoin que de ces quatre rotations pour maintenir la proprit AVL sur un arbre binaire ordonn. Aprs lajout en feuille dun nouvel lment, la vrication de la proprit AVL se fait en remontant la branche partir de la nouvelle feuille insre jusqu la racine de larbre. Linsertion provoque au maximum un dsquilibre et ne demande au plus quune rotation simple ou double pour rquilibrer larbre. Le cot du rquilibrage est O(1). Aprs la suppression dun lment, la vrication de la proprit AVL se fait, comme prcdemment, en remontant vers la racine depuis le nud supprim. Mais, contrairement

20.5

Les arbres AVL

259

(a)

(b)
F IG . 20.6 Rotation double gauche.

(a)

(b)
F IG . 20.7 Rotation double droite.

linsertion, une suppression peut provoquer (dans le pire des cas) une rotation de chaque nud de la branche. Le cot du rquilibrage est O(log2 n). Dun point de vue exprimental, la cration darbres AVL ordonns avec des cls donnes dans un ordre alatoire, provoque environ une rotation (simple ou double en proportion gale) pour deux insertions, et une rotation (simple ou double, mais plus souvent simple que double) pour quatre suppressions.

20.5.2

Mise en uvre

Les arbres AVL sont des arbres binaires sur lesquels les rotations dcrites prcdemment devront tre possibles. Nous appellerons ces arbres binaires arbres restructurables. Pour les reprsenter, nous dnissons linterface gnrique ArbreRestructurable qui tend la

260

Chapitre 20 Tables

classe ArbreBinaire. Elle propose limplmentation les mthodes qui assurent les quatre rotations. Cette interface est la suivante :
public interface ArbreRestructurable<E> extends ArbreBinaire<E> { public ArbreRestructurable<E> rotationSimpleGauche(); public ArbreRestructurable<E> rotationSimpleDroite(); public ArbreRestructurable<E> rotationDoubleGauche(); public ArbreRestructurable<E> rotationDoubleDroite(); }

La mise en uvre des arbres AVL seffectue plus commodment laide de structures chanes. Limplmentation de linterface ArbreRestructurable se fait par hritage de la classe ArbreBinaireChan. Les quatre mthodes de rotation modient larbre courant selon les rgles donnes dans la section prcdente.
public class ArbreRestructurableChan<E> extends ArbreBinaireChan<E> implements ArbreRestructurable<E> { public ArbreRestructurableChan(E e) { super(e); } // Antcdent : larbre courant possde un sous-arbre gauche non vide // Rle : effectue une rotation entre le nud courant et // son sous-arbre gauche public ArbreRestructurable<E> rotationSimpleGauche() { assert !sag().estVide(); ArbreRestructurable<E> a = (ArbreRestructurable<E>) sag(); changerSag(a.sad()); a.changerSad(this); return a; } // Antcdent : larbre courant possde un sous-arbre droit non vide // Rle : effectue une rotation entre le nud courant et // son sous-arbre droit public ArbreRestructurable<E> rotationSimpleDroite() { assert !sad().estVide(); ArbreRestructurable<E> a = (ArbreRestructurable<E>) sad(); changerSad(a.sag()); a.changerSag(this); return a; } public ArbreRestructurable<E> rotationDoubleGauche() { changerSag(((ArbreRestructurable<E>) sag()).rotationSimpleDroite()); return rotationSimpleGauche(); } public ArbreRestructurable<E> rotationDoubleDroite() { changerSad(((ArbreRestructurable<E>) sad()).rotationSimpleGauche()); return rotationSimpleDroite(); } } // fin classe ArbreRestructurableChan

20.5

Les arbres AVL

261

La mise en uvre dune table laide dun arbre AVL ordonn est dcrite par la classe gnrique ArbreAVLChan. Puisquun arbre AVL ordonn est un arbre ordonn particulier, cette classe hrite naturellement de la classe ArbreOrdonnChan. Ceci nous permettra en particulier de ne pas avoir rcrire la mthode rechercher.
public class ArbreAVLChan<V,C> extends ArbreOrdonnChan<V,C> implements Table<V,C> { public ArbreAVLChan(Comparateur<C> cmp) { super(cmp); } ... } // fin classe ArbreAVLChan

La proprit AVL requiert la connaissance de la hauteur des arbres manipuls. Pour des raisons defcacit, il nest pas question de recalculer, par un parcours de larbre, cette hauteur chaque fois que cela est ncessaire. Au contraire, nous allons associer chaque nud un attribut qui mmorisera la hauteur de larbre. Cet attribut est simplement dni dans une classe lmentAVL, hritire de la classe lment.
public class lmentAVL<V,C> extends lment<V,C> { protected int hauteur = 0; public lmentAVL(V v, C c) { super(v,c); } public lmentAVL(lment<V,C> e) { super(e.valeur(), e.cl()); } public int hauteur() { return this.hauteur; } public void changerHauteur(int h) { this.hauteur = h; } }

Les mthodes prives rquilibrerG et rquilibrerD sont utilises par les mthodes ajouter et supprimer. Leur rle est de calculer la nouvelle hauteur de larbre binaire a pass en paramtre et de vrier sil possde la proprit AVL. Sil y a un dsquilibre, elles procdent la rotation adquate. Les nouvelles hauteurs des sous-arbres modis sont recalcules. Par convention, on considre que la hauteur dun arbre vide est gale 1.
private int hauteur(ArbreBinaire<lment<V,C>> a) { return a.estVide() ? -1 : ((lmentAVL<V,C>) a.racine()).hauteur(); } private void calculerHauteur(ArbreBinaire<lment<V,C>> a) { ((lmentAVL<V,C>) a.racine()).changerHauteur (1 + Math.max(hauteur(a.sag()), hauteur(a.sad()))); }

262

Chapitre 20 Tables

private ArbreBinaire<lment<V,C>> rquilibrerG(ArbreBinaire<lment<V,C>> a) { if (hauteur(a.sag()) - hauteur(a.sad()) == 2) { // larbre a nest plus quilibr, // le sous-arbre gauche est trop grand a = hauteur(a.sag().sag()) >= hauteur(a.sag().sad()) ? ((ArbreRestructurable<lment<V,C>>) a).rotationSimpleGauche() // sinon double rotation gauche : ((ArbreRestructurable<lment<V,C>>) a).rotationDoubleGauche(); // calculer les hauteurs des nouveaux // sous-arbres gauche et droit de a calculerHauteur(a.sag()); calculerHauteur(a.sad()); } // calculer la nouvelle hauteur de a calculerHauteur(a); return a; } private ArbreBinaire<lment<V,C>> rquilibrerD(ArbreBinaire<lment<V,C>> a) { if (hauteur(a.sad()) - hauteur(a.sag()) == 2) { // larbre a nest plus quilibr, // le sous-arbre droit est trop grand a = hauteur(a.sad().sad()) >= hauteur(a.sad().sag()) ? ((ArbreRestructurable<lment<V,C>>) a).rotationSimpleDroite() // sinon double rotation droite : ((ArbreRestructurable<lment<V,C>>) a).rotationDoubleDroite(); // calculer les hauteurs des nouveaux sous-arbres // gauche et droit de a calculerHauteur(a.sag()); calculerHauteur(a.sad()); } // calculer la nouvelle hauteur de a calculerHauteur(a); return a; }

Les mthodes ajouter et supprimer suivent les mmes algorithmes rcursifs que ceux donns la section 20.4 pour les arbres ordonns simples, mais dans lesquels sont insrs les appels aux mthodes de vrication de la proprit AVL, rquilibrerG et rquilibrerD. Les vrications sont faites aprs les appels rcursifs, pour tre excutes en remontant , du point dinsertion ou de suppression vers la racine.
public void ajouter(lment<V,C> e) { if (! comp.comparable(e.cl())) throw new ClIncomparableException(); r = ajouter(r, e); }

20.5

Les arbres AVL

263

private ArbreBinaire<lment<V,C>> ajouter(ArbreBinaire<lment<V,C>> a, lment<V,C> e) { if (a.estVide()) return new ArbreRestructurableChan<lment<V,C>> (new lmentAVL<V,C>(e)); // a non vide if (comp.suprieurOugal(a.racine().cl(), e.cl())) { // cl du nud courant e.cl // ajouter dans le sous-arbre gauche a.changerSag(ajouter(a.sag(), e)); a = rquilibrerG(a); } else { // cl du nud courant < e.cl // ajouter dans le sous-arbre droit a.changerSad(ajouter(a.sad(), e)); a = rquilibrerD(a); } return a; } public void supprimer(C cl) throws ClNonTrouveException { if (! comp.comparable(cl)) throw new ClIncomparableException(); r = supprimer(r, cl); } private ArbreBinaire<lment<V,C>> suppmax(ArbreBinaire<lment<V,C>> a, ArbreBinaire<lment<V,C>> o) { if (!a.sad().estVide()) { a.changerSad(suppmax(a.sad(),a)); return rquilibrerG(a); } else { // larbre a a pour racine le max o.changerRacine(a.racine()); return a.sag(); } } private ArbreBinaire<lment<V,C>> supprimer(ArbreBinaire<lment<V,C>> a, C cl) { if (a.estVide()) // arbre vide non trouve throw new ClNonTrouveException(); C clDuNoeud = a.racine().cl(); if (comp.infrieur(cl,clDuNoeud)) { // cl du nud courant e.cl // supprimer dans le sous-arbre gauche a.changerSag(supprimer(a.sag(), cl)); a = rquilibrerD(a); }

264

Chapitre 20 Tables

else if (comp.suprieur(cl,clDuNoeud)) { // cl du nud courant < e.cl // supprimer dans le sous-arbre droit a.changerSad(supprimer(a.sad(), cl)); a = rquilibrerG(a); } else // clDuNoeud = cl est trouv if (a.sad().estVide()) // a = sag a = a.sag(); else if (a.sag().estVide()) // a = sad a = a.sad(); else { // a possde deux sous-arbres non vides a.changerSag(suppmax(a.sag(), a)); a = rquilibrerD(a); } return a; } // fin supprimer

20.6
20.6.1

ARBRES 2-3-4 ET BICOLORES


Les arbres 2-3-4

Les arbres que nous avons tudis jusqu prsent possdent une seule cl (et sa valeur associe) par nud. Mais en fait, rien ninterdit de mettre plusieurs cls dans un nud, cest justement cette possibilit qui garantira lquilibre des arbres que nous allons prsenter dans cette section. On appelle arbre 2-3-4 4 un arbre quilibr ordonn dont chaque nud contient une, deux ou trois cls. Il est remarquable que toutes les branches, de la racine aux feuilles, de cet arbre possdent la mme longueur. Son nom vient du fait quun nud une cl possde deux sous-arbres, un nud deux cls possde trois sous-arbres, et un nud trois cls possde quatre sous-arbres. Aucun nud interne ne possde de sous-arbres vides et nous appellerons ces nuds, respectivement, nud-2, nud-3 et nud-4. Les cls dans un arbre 2-3-4 sont ordonnes de telle faon quun nud-2 possde un sous-arbre pour les cls infrieures sa cl, et un autre pour les cls suprieures. Un nud-3 possde trois sous-arbres, un pour les cls infrieures ses deux cls, un pour les cls comprises entre ses deux cls, et un troisime pour les cls suprieures ses deux cls. Enn, un nud-4 possde quatre sous-arbres, un sous-arbre pour les cls infrieures sa cl, un autre pour les cls suprieures, et deux autres pour les deux intervalles dnis par ses trois cls. La gure 20.8 montre un exemple darbre 2-3-4, form de neuf nud-2, de quatre nud-3 et de trois nud-4, dont les cls sont ordonnes selon ces rgles.
4 Les arbres 2-3-4 sont des cas particuliers de B-arbres, arbres quilibrs invents par R. BAYER et E. M C C REIGHT en 1972, pour permettre des recherches efcaces dans des tables places en mmoire secondaire (e.g. disques). Certains systmes de chiers, comme Btrfs, utilisent les B-arbres comme reprsentation interne.

20.6

Arbres 2-3-4 et bicolores

265

10

32 60

12 23

51

65 81 87

10

11

15

26

42

49

55

62 64

70

85

91 95 98

F IG . 20.8 Un arbre 2-3-4.

La forme parfaitement quilibre des arbres 2-3-4 garantit une hauteur de larbre comprise entre log4 (n+1) et log2 (n+1) , o n est le nombre dlments contenus dans larbre. Le cot dune recherche est donc au pire gal 3( log2 n + 1), si lon procde une recherche linaire de llment dans chaque nud. La complexit dune recherche dans un arbre 2-3-4 est donc O(log2 n). Lquilibre des arbres 2-3-4 est maintenu par les oprations dajout et de suppression de cls. Lajout dun lment dans un arbre 2-3-4 se fait en feuille. Si cette feuille est un nud-2 ou un nud-3, llment est simplement insr sa place, ce qui transforme la feuille en un nud-3 ou un nud-4. En revanche, un problme se pose lorsque la feuille o ajouter le nouvel lment est un nud-4. Il nest en effet pas possible de placer un nouveau nud-2 sous ce nud-4 puisque cela remettrait en cause la forme quilibre de larbre. Une premire solution, dite ascendante, consiste scinder la feuille en deux nud-2 dont les valeurs sont celles de llment de gauche et de droite du nud-4, et dinsrer ensuite llment du milieu du nud-4 dans son pre. Lopration dajout peut alors placer le nouvel lment dans lun des deux nouveaux nud-2 crs en feuille. Le nud pre a t transform en nud-3 ou nud-4, selon quil tait au pralable un nud-2 ou nud-3. Si le pre tait lui-mme un nud-4, cette opration devra tre renouvele avec son propre pre, et ainsi de suite jusqu la racine si ncessaire. La seconde solution, dite descendante, scinde de faon similaire les nud-4, mais cette fois-ci lors de la recherche de la feuille o insrer llment. Puisque les nud-4 sont transforms en descendant la branche, le pre de la feuille nud-4 dans lequel on insre son lment central ne peut pas tre un nud-4. La gure 20.9 montre comment le nud-4 (15,20,30) est scind lorsque lalgorithme dajout le traverse. Lorsque la racine de larbre est un nud-4, sa scission a pour effet de crer un nouveau nud-2 et la hauteur de larbre est alors augmente de un. Dans le pire des cas, si tous les nuds de la branche sont des nud-4, il faut procder log2 n + 1 scissions. Notez que la scission dun nud-4 est une opration relativement coteuse, mais que de faon exprimentale, nous avons mesur que les ajouts dlments dont les cls sont tires de faon alatoire provoquent en moyenne une scission pour deux ajouts. Si on autorise lajout de plusieurs lments de mme cl, ceux-ci ne seront pas ncessairement placs cte cte dans larbre. Une procdure de recherche qui renverrait lensemble des lments de mme cl devra alors poursuivre la recherche dans plusieurs sous-arbres partir du nud qui contient le premier lment avec la cl recherche.

266

Chapitre 20 Tables

13

13 20

10

15

20

30

10

15

30

F IG . 20.9 clatement dun nud-4.

Dune faon gnrale, la suppression dun lment consiste le remplacer par llment de cl immdiatement infrieure5 . Ce dernier se trouve ncessairement tre le plus droite dans la feuille la plus droite en parcourant le sous-arbre des lments de cls infrieures celle de llment supprimer. Si cette feuille est un nud-3 ou un nud-4, la suppression de llment le plus droite ne pose aucune difcult. En revanche, si la feuille est un nud-2, il faut procder soit une permutation, soit une fusion. Si son frre gauche est un nud-3 ou un nud-4, on fait une permutation dlments entre la feuille nud-2, son pre et son frre gauche, comme le montre par exemple la gure 20.10 dans le cas de la suppression de la cl 45.

45

30

13

10

10

30

13

F IG . 20.10 Suppression de la cl 45 provoquant une permutation.

Si le frre gauche est un nud-2, la permutation prcdente nest plus possible, et on remplace llment de la feuille nud-2 par llment le plus droite de son pre, puis on procde la fusion de la feuille nud-2 et de son frre gauche. Le nombre dlments du nud pre est diminu de un. La gure 20.11 montre un exemple de fusion. Notez que si le pre est lui-mme un nud-2, il faut recommencer cette opration de fusion au niveau du pre. De proche en proche, elle peut se propager jusqu la racine de larbre 2-3-4, et dans ce
5 On

peut tout aussi bien choisir llment de cl immdiatement suprieure ou gale.

20.6

Arbres 2-3-4 et bicolores

267

cas la hauteur de larbre est diminue de un. Dans le pire des cas, le nombre de fusions est gal la hauteur de larbre 2-3-4 plus un.

45

30

13

10
fusion

30

10 13

F IG . 20.11 Suppression de la cl 45 provoquant une fusion.

La propagation de la fusion souffre une exception dans le cas o le nud fusionner est un nud-4. Par exemple dans larbre de la gure 20.12, la suppression de la cl 8 provoque la fusion des cls 5 et 10, et se propage sur le nud pre. La cl 13 remplace la cl 8, et doit tre fusionne avec son voisin (i.e. 20 21 35). Mais, ce nud est un nud-4 et il nest pas possible, par dnition, de crer un nud-5 (i.e. 13 20 31 45). La solution consiste alors transfrer le premier lment du nud-3 dans son pre. Si le pre tait lui-mme un nud-2, on poursuivrait par une fusion avec son voisin (dans lexemple, 13 avec 31 et 45).

13 72

20 72

20

31

45

13

31 45

10

16 18

24

39

54

10

16 18

24

39

54

F IG . 20.12 Suppression de la cl 8.

Les rsultats exprimentaux de suppressions alatoires de cls montrent environ un transfert pour quatre suppressions et une fusion pour six suppressions.

20.6.2

Mise en uvre en Java

Il est possible de reprsenter les arbres 2-3-4 par des arbres gnraux tels que nous les avons dnis au chapitre 19. La mise en uvre de linterface Table laide dun arbre 2-3-4 est dcrite par la classe gnrique Arbre234.

268

Chapitre 20 Tables

public class Arbre234<V,C> implements Table<V,C> { protected Arbre<Valeur234<V,C>> r; // racine de larbre 2-3-4 protected Comparateur<C> comp; public Arbre234(Comparateur<C> cmp) { r=null; comp=cmp; } ... }

Les lments et leurs cls, contenus dans chaque nud de larbre, sont rangs dans une liste ordonne mise en uvre laide dun tableau trois composants. On les reprsente par une classe Valeur234 qui hrite de ListeOrdonneTableau. On munit cette classe dune mthode particulire de recherche linaire qui renvoie le rang dans la liste de llment recherch si celui-ci est prsent, ou celui de llment qui lui est immdiatement suprieur dans le cas contraire.
class Valeur234<V,C> extends ListeOrdonneTableau<V,C> { Valeur234(lment<V,C> e, Comparateur<C> cmp) { super(3,cmp); super.ajouter(1,e); } int rechercher(Comparateur<C> cmp, C cl) { int i = 1; while (i <= longueur() && cmp.suprieur(cl,ime(i).cl())) i++; return i; } }

La mthode rechercher dans un arbre 2-3-4 est donne ci-dessous. Dans chaque nud, on effectue la recherche linaire de la cl. Si cette recherche choue, r donne le rang du ime sous-arbre dans lequel doit se poursuivre rcursivement la recherche ; mais si le nud courant est une feuille, la recherche dans larbre sachve et choue.
public lment<V,C> rechercher(C cl) throws ClNonTrouveException { if (! comp.comparable(cl)) throw new ClIncomparableException(); if (estVide()) // la table est vide throw new ClNonTrouveException(); return rechercher(r, cl); } private lment<V,C> rechercher(Arbre<Valeur234<V,C>> a, C cl) throws ClNonTrouveException { // rechercher dans la cl dans le noeud courant. Valeur234<V,C> n = a.racine(); int r = n.rechercher(comp, cl); // r>n.longueur() ou cl n.ime(r).cl() if (r <= n.longueur() && comp.gal(cl, n.ime(r).cl())) // cl trouve

20.6

Arbres 2-3-4 et bicolores

269

return n.ime(r); // sinon cl < n.ime(r).cl() if (!a.estUneFeuille()) // a nest pas une feuille poursuivre la recherche return rechercher(a.fort().imeArbre(r), cl); // sinon cl non trouve throw new ClNonTrouveException(); }

Les mthodes ajouter et supprimer sont plus dlicates programmer, et plus particulirement la seconde. Elles constituent dailleurs un excellent exercice de programmation laiss au lecteur.

20.6.3

Les arbres bicolores

La mise en uvre prcdente dun arbre 2-3-4 par un arbre gnral rend les algorithmes dajout et de suppression complexes et nalement moins efcaces que ceux des arbres AVL, et peut-tre mme que ceux des arbres binaires simples. Au chapitre 19, nous avons vu que tout arbre gnral possdait une reprsentation binaire. Les arbres bicolores, appels galement arbres rouge-noir, sont des arbres binaires ordonns qui offrent une reprsentation particulirement efcace des arbres 2-3-4. Pour bien comprendre les oprations dajout et de suppression dcrites dans cette section, il est essentiel davoir clairement lesprit la correspondance entre les nuds des arbres bicolores avec ceux des arbres 2-3-4. Les quivalents binaires de chaque type de nud dun arbre 2-3-4 sont donns par la gure 20.13. Les frres dans un nud-3 ou un nud-4 sont coloris en rouge (liens gras et cercles doubles dans la gure), alors que lan est colori en noir (liens maigres et cercles simples). La gure 20.14 montre les reprsentations 2-3-4 et bicolore dun arbre dans lequel on a insr les cls de 1 8 en ordre croissant. partir de ces rgles de correspondance entre nuds, il est trs facile de dduire que la racine dun arbre bicolore est noire, quun nud rouge est ncessairement le ls dun nud noir, et que toutes les branches de larbre possdent le mme nombre de nuds noirs. Ce dernier point est d au fait que toutes les branches dun arbre 2-3-4 ont la mme longueur, et quil ne peut y avoir dans un arbre bicolore quun seul nud noir par nud 2-3-4. Enn, on en dduit que la hauteur dun arbre bicolore n lments est au plus gale au double de larbre 2-3-4 correspondant, cest--dire quelle est O(log2 n). La mthode de recherche dans un arbre bicolore est identique celle dans un arbre binaire standard, et la forme quilibre de larbre garantit une complexit de recherche de lordre de O(log2 n) pour un arbre n lments. Lajout dun lment dans un arbre bicolore se fait en feuille et pose le mme problme que dans un arbre 2-3-4. Il sagit de scinder les quivalents binaires des nud-4, soit lors de la recherche de la feuille o insrer llment (algorithme descendant), soit aprs linsertion dans une feuille nud-4 (algorithme ascendant). Pour un arbre bicolore, lopration de scission consiste simplement colorier en noir les deux frres, et en rouge lan puisque ce dernier est insr dans son pre. La gure 20.15 montre cette opration de changement de couleur.

270

Chapitre 20 Tables

4 ou 6 4

F IG . 20.13 Correspondances entre nuds 2-3-4 et nuds bicolores.

8
F IG . 20.14 Le mme arbre sous forme 2-3-4 et bicolore.

Notez que si lan est la racine de larbre, ce nud devra tre colori nouveau en noir, puisque le changement de couleur correspond la scission dun nud-4 la racine dun arbre 2-3-4. Souvenez-vous que cest lunique cas o la hauteur dun arbre 2-3-4 est augmente de un.

20.6

Arbres 2-3-4 et bicolores

271

F IG . 20.15 Changement de couleur dun nud-4.

Nous avons vu quun arbre bicolore ne peut possder deux nuds rouges conscutifs. Lopration de changement de couleur prcdente peut violer cette rgle si le pre du nud qui a t colori en rouge est lui-mme un nud rouge. On limine ce problme par les rotations simples ou doubles identiques celles prsentes dans la section 20.5.1. La gure 20.16 montre les quatre situations possibles et les rotations faire. Notez que le grand-pre est par dnition ncessairement un nud noir. Aprs chaque rotation, le pre est colori en noir, et le grand-pre en rouge. Pour quon puisse supprimer un lment, celui-ci doit tre prsent dans larbre bicolore. Comme pour un simple arbre binaire ordonn, il ne sera possible de supprimer quun nud ayant au plus un sous-arbre. Si le nud qui le contient possde deux sous-arbres, on remplacera llment supprimer par llment de cl immdiatement infrieure ou gale6 situ le plus droite dans le sous-arbre gauche. Cest ce nud avec au plus un sous-arbre qui sera supprim. Sa suppression consiste simplement lier son pre avec son unique ls gauche. Si le nud supprim est rouge, ou si son ls unique est rouge (le nud supprim tait donc noir), on colorie en noir le ls et la suppression est termine. Notez que cela correspond la suppression dun lment dans un nud-3 ou un nud-4. Dans le cas contraire, le nud supprim et son ls unique sont noirs. Cela correspond la suppression dun nud-2 dans un arbre 2-3-4, que nous avions traite par une permutation ou une fusion selon la nature de son frre. Pour un arbre bicolore, nous envisagerons trois cas de gure. Premier cas, le frre du nud supprim est noir et possde un ls rouge. Le frre est un quivalent nud-3 ou nud-4, et la suppression tait assure par une permutation. Pour un arbre bicolore, il faut produire une rotation simple ou double, du ls rouge, du frre et du pre du nud supprim. Aprs la rotation, le nouveau pre prend la couleur de lancien pre, ses ls gauche et droit sont coloris en noir, tout comme le ls unique. La gure 20.17 montre un exemple de permutation, et sa correspondance avec un arbre 2-3-4 ; la suppression de llment 30 provoque une rotation double gauche. Deuxime cas, le frre du nud supprim est noir et ses deux ls sont noirs, cest--dire que le frre est lquivalent dun nud-2. On colorie en noir le ls unique et en rouge le frre. Si le pre du nud supprim est rouge, il suft de colorier ce pre en noir, pour obtenir leffet dune fusion dans larbre quivalent 2-3-4. La gure 20.18 met en vidence ce cas lorsquon supprime llment 13. En revanche, si le pre est noir et quil nest pas la racine de larbre, on considre nouveau les trois cas de gure son niveau. Ceci consiste faire une propagation au niveau du parent. Le nombre de propagations possibles est la hauteur de larbre, O(log2 n).
6 Ou,

au choix, de cl immdiatement suprieure ou gale situe situ le plus gauche dans le sous-arbre droit.

272

Chapitre 20 Tables

grandpre

15
pre

rotation simple gauche

10

pre

10

15

grandpre

rotation simple droite

pre

pre

pre

grandpre

rotation double gauche

pre

pre

pre

3 6

grandpre

rotation double droite

6 1

pre

9 6

pre

F IG . 20.16 Suppression de 2 nuds rouges conscutifs.

Troisime cas, le frre du nud supprim est rouge. Il sagit de changer sa couleur en noir pour tre en mesure dappliquer ensuite lun des deux cas prcdents. Pour cela, on transforme larbre laide dune rotation simple. La rotation simple fait intervenir le pre du nud supprim, le frre, et son ls gauche (si le frre est un ls gauche) ou son ls droit dans le cas contraire. Aprs la rotation, le frre devient le nouveau pre, il est colori en noir,

20.6

Arbres 2-3-4 et bicolores

273

rotation double gauche

13

pre

10

frre

30
fils unique

13

fils rouge

10

permutation

13

10

10

30

13

F IG . 20.17 Suppression de 30 dans larbre bicolore, et sa correspondance 2-3-4.

et lancien pre est colori en rouge. Notez quaprs cette transformation, lapplication du deuxime cas nentranera pas de propagation puisque le pre est rouge. Dans la gure 20.19, le frre droit de llment 5 supprim est rouge. La rotation simple droite rorganise larbre de telle faon que le nouveau frre 13 est un nud noir. Le deuxime cas peut maintenant tre appliqu. La complexit de la suppression est O(log2 n). Pour chaque suppression, il y a O(log2 n) propagations, et au plus une rorganisation et une permutation, soit une rotation simple, plus une rotation simple ou double. Les mesures exprimentales dajout de cls dans un ordre alatoire donnent environ deux rotations, une simple et une double, pour cinq insertions (ce qui est un peu mieux que pour un arbre AVL). Pour les suppressions, le nombre de rotations est semblable celui des arbres AVL, cest--dire une rotation pour quatre suppressions.

20.6.4

Mise en uvre en Java

Comme les arbres AVL, les arbres bicolores sont reprsents par des arbres restructurables chans sur lesquels les rotations, simples et doubles, sont possibles. Limplantation dune table laide dun arbre bicolore est dcrite par la classe gnrique ArbreBicoloreChan qui hrite de la classe ArbreOrdonnChan et rednit ses deux mthodes ajouter et supprimer, la mthode rechercher tant obtenue par

274

Chapitre 20 Tables

10

pre

10

frre

13
fils unique

fusion

10

13

10

F IG . 20.18 Suppression de 13 dans larbre bicolore, et sa correspondance 2-3-4.


rotation simple droite

pre

10

5
fils unique

10

frre

13

13

fils droit

F IG . 20.19 Suppression de 5 dans larbre bicolore.

hritage.
public class ArbreBicoloreChan<V,C> extends ArbreOrdonnChan<V,C> implements Table<V,C> { public ArbreBicoloreChan(Comparateur<C> cmp) { super(cmp); } ... }

La couleur des nuds est reprsente par le type numr Bicolore qui dnit les constantes rouge et noir. Elle est conserve dans un attribut associ chacun des l-

20.6

Arbres 2-3-4 et bicolores

275

ments des nuds de larbre. Comme pour les arbres AVL, on dnit une classe hritire de la classe lment, que nous appellerons lmentBicolore, dans laquelle on dclare un attribut couleur et les mthodes qui permettent daccder sa valeur ou de la changer. Notez que, par convention, la couleur dun arbre vide est noire.
public enum Bicolore { noir, rouge } public class lmentBicolore<V,C> extends lment<V,C> { protected Bicolore couleur; public lmentBicolore(lment<V,C> e, Bicolore c) { super(e.valeur(), e.cl()); couleur=c; } public lmentBicolore(lment<V,C> e) { super(e.valeur(), e.cl()); couleur=Bicolore.rouge; } public Bicolore couleur() { return couleur; } public void changerCouleur(Bicolore c) { couleur=c; } }

La mthode ajouter, donne ci-dessous, parcourt de faon itrative la branche lextrmit de laquelle le nouvel lment sera ajout. En plus du nud courant, elle mmorise les nuds pre, grand-pre et arrire-grand-pre laide de trois attributs privs. Lors de la recherche de la feuille o a lieu linsertion, chaque quivalent binaire dun nud-4 (racine des sous-arbres gauche et droit rouges) est scind (algorithme descendant) grce lappel de la mthode prive scinderNoeud. Un dernier appel cette mthode est ncessaire aprs lajout en feuille de llment.
private ArbreBinaire<lment<V,C>> pre, grandPre, arrireGrandPre; public void ajouter(lment<V,C> e) { if (! comp.comparable(e.cl())) throw new ClIncomparableException(); if (r.estVide()) r=new ArbreRestructurableChan<lment<V,C>>( new lmentBicolore<V,C>(e, Bicolore.noir)); else { // larbre nest pas vide ArbreBinaire<lment<V,C>> courant; courant=pre=grandPre=arrireGrandPre=r; do { if (estRouge(courant.sag()) && estRouge(courant.sad())) // quivalent nud-4 scinder scinderNoeud(courant); arrireGrandPre=grandPre; grandPre=pre; pre=courant; courant = comp.suprieurOugal(cl(courant), e.cl()) ?

276

Chapitre 20 Tables

// cl du nud courant e.cl // ajouter dans le sous-arbre gauche courant.sag() : // cl du nud courant < e.cl // ajouter dans le sous-arbre droit courant.sad(); } while (!courant.estVide()); // pre dsigne la feuille o attacher le nouveau nud if (comp.suprieurOugal(cl(pre), e.cl())) { pre.changerSag(new ArbreRestructurableChan<lment<V,C>>( new lmentBicolore<V,C>(e))); courant=pre.sag(); } else { pre.changerSad(new ArbreRestructurableChan<lment<V,C>>( new lmentBicolore<V,C>(e))); courant=pre.sad(); } scinderNoeud(courant); } }

La mthode prive scinderNoeud commence par changer les couleurs du nud courant et des nuds de ses sous-arbres gauche et droit selon la rgle donne plus haut. Si son pre est rouge, il y a donc deux nuds rouges conscutifs, et une rotation simple ou double est alors ncessaire pour rendre larbre nouveau bicolore. Cest la mthode restructurer qui choisit la rotation appliquer. Aprs la rotation, le pre et le grand-pre changent leur tour de couleur.
private void scinderNoeud(ArbreBinaire<lment<V,C>> a) { enRouge(a); enNoir(a.sag()); enNoir(a.sad()); if (a == r) enNoir(r); else // y a-t-il deux nuds rouges conscutifs ? // note : si pre=r, pas de rotation car la racine est noire if (estRouge(pre)) { ArbreBinaire<lment<V,C>> np = restructurer(a, pre, grandPre, arrireGrandPre); // changer les couleurs enNoir(np); enRouge(np.sag()); enRouge(np.sad()); } }

Le type de rotation effectuer est fonction de la structure de larbre. La mthode


restructurer suivante applique les rotations simples ou doubles comme lindique la -

gure 20.16 la page 272.


private ArbreBinaire<lment<V,C>> restructurer( ArbreBinaire<lment<V,C>> a, ArbreBinaire<lment<V,C>> p, ArbreBinaire<lment<V,C>> gp, ArbreBinaire<lment<V,C>> agp) {

20.6

Arbres 2-3-4 et bicolores

277

if (p.sag() == a) p = gp.sag() == p ? ((ArbreRestructurablee<lment<V,C>>) : ((ArbreRestructurablee<lment<V,C>>) else // (p.sad() == a) p = gp.sad() == p ? ((ArbreRestructurablee<lment<V,C>>) : ((ArbreRestructurablee<lment<V,C>>)

gp).rotationSimpleGauche() gp).rotationDoubleDroite();

gp).rotationSimpleDroite() gp).rotationDoubleGauche();

// accrocher larbre rquilibr larrire-grand-pre sil existe // sinon, le pre est la nouvelle racine de larbre if (gp == r) r=p; else if (agp.sad() == gp) agp.changerSad(p); else agp.changerSag(p); return p; }

La mthode supprimer est donne ci-dessous dans sa totalit. La propagation ncessite la connaissance des ascendants du nud supprim. Ceux-ci sont mmoriss dans une pile lors du parcours de la branche qui conduit au nud supprimer. La fonction parent renvoie le pre du nud courant, situ en sommet de pile.
public void supprimer(Object cl) throws ClNonTrouveException { if (! comp.comparable(cl)) throw new ClIncomparableException(); if (estVide()) // arbre vide cl non trouve throw new ClNonTrouveException(); // larbre nest pas vide Pile<ArbreBinaire<lment<V,C>>> lesPres = new PileChane<ArbreBinaire<lment<V,C>>>(); ArbreBinaire<lment<V,C>> courant=r; boolean trouve=false; do { if (comp.gal(cl(courant), cl)) trouve=true; else { lesPres.empiler(courant); courant = (comp.suprieurOugal(cl(courant), cl)) ? courant.sag() // cl du nud courant cl : courant.sad(); // cl du nud courant < cl } } while (!trouve && !courant.estVide()); // courant dsigne le nud contenant la cl supprimer // OU cl non trouve if (!trouve) throw new ClNonTrouveException(); // rechercher le maximum du sous-arbre gauche de courant ArbreBinaire<lment<V,C>> noeudSupp =courant, suivant=noeudSupp.sag(); while (!suivant.estVide()) { lesPres.empiler(noeudSupp); noeudSupp=suivant; suivant=suivant.sad(); }

278

Chapitre 20 Tables

// noeudSupp dsigne le nud supprimer contenant la cl // immdiatement infrieure ou gale celle de courant if (r == noeudSupp) { // on supprime la racine, son sous-arbre gauche // est ncessairement vide r=noeudSupp.sad(); enNoir(r); return; } Bicolore couleurEltSupp=couleur(noeudSupp), ancienneCouleurCourant=couleur(courant); // mettre la valeur de noeudSupp dans courant if (courant!=noeudSupp) { courant.racine().changerValeur(noeudSupp.racine().valeur()); changerCouleur(courant, ancienneCouleurCourant); } // prendre le fils unique du nud supprim, // son pre et son grand-pre ArbreBinaire<lment<V,C>> filsNoeudSupp = (courant == noeudSupp) ? noeudSupp.sad() : noeudSupp.sag(); pre=parent(lesPres); grandPre=parent(lesPres); // suppression de noeudSupp // quel sous-arbre du pre accrocher son fils ? if (pre.sad() == noeudSupp) pre.changerSad(filsNoeudSupp); else pre.changerSag(filsNoeudSupp); if (estRouge(filsNoeudSupp)||couleurEltSupp==Bicolore.rouge) enNoir(filsNoeudSupp); else // le nud supprim est noir et son fils aussi do { ArbreBinaire<lment<V,C>> frre = quelFrre(filsNoeudSupp, pre); if (estRouge(frre)) { // le frre est rouge rorganisation ArbreBinaire<lment<V,C>> filsFrreRouge= frre == pre.sad() ? frre.sad() : frre.sag(); grandPre = restructurer(filsFrreRouge, frre, pre, grandPre); enNoir(grandPre); enRouge(pre); } else { // le frre est noir if (estRouge(frre.sag()) || estRouge(frre.sad())) { // un de ses fils est rouge permutation ArbreBinaire<lment<V,C>> filsRouge = estRouge(frre.sag()) ? frre.sag() : frre.sad(), a=restructurer(filsRouge, frre, pre, grandPre); changerCouleur(a, couleur(pre)); enNoir(a.sag()); enNoir(a.sad()); enNoir(filsNoeudSupp); return; } // ses deux fils sont noirs

20.7

Tables dadressage dispers

279

enNoir(filsNoeudSupp); enRouge(frre); if (estRouge(pre)) { // fusion enNoir(pre); return; } else { // propagation si le pre nest pas la racine if (pre == r) return; filsNoeudSupp=pre; pre=grandPre; grandPre=parent(lesPres); } } } while (true); } // fin supprimer

20.7

TABLES DADRESSAGE DISPERS

Lide des tables dadressage dispers7 est daccder, en une seule comparaison, un lment de la table grce une fonction calcule partir de sa cl. Cette fonction est utilise par les oprations dajout, de recherche et de suppression. De faon formelle, il sagit de dnir une fonction h, appele fonction dadressage, telle que : h : Cl e Place

o Place est lensemble des positions possibles pour un lment dans la table. Le plus souvent, les tables dadressage dispers sont reprsentes par des tableaux, et le rle de la fonction h est de retourner une valeur prise dans le type des indices du tableau. La convention habituelle est de choisir les indices sur lintervalle [0, m 1], o m est le nombre de composants du tableau. Une fonction dadressage idale renvoie une place diffrente pour chacune des cls des lments. Prenons une table qui possde onze places, dans laquelle on dsire insrer sept lments dont les cls alphabtiques sont les suivantes : Louise, Germaine, Paul, Maud, Pierre, Jacques et Monique. Lvaluation dune fonction dadressage idale pour ces cls renvoie, par exemple, les sept indices suivants 6, 2, 8, 3, 1, 7 et 10. La gure 20.20 page 280 montre la table avec les lments leur place (pour simplier, seule la cl est afche). Notez que les lments dans les tables dadressage ne sont pas ordonns et, contrairement aux reprsentations de table vues prcdemment, le cot de la recherche dans une table dadressage dispers est constant et ne dpend pas du nombre dlments dans la table. Pour une fonction dadressage idale, ce cot est gal au cot de calcul de la fonction h, auquel
7 Appeles en anglais, hash tables, car il sagit de hacher ou de dcouper la cl pour nen conserver quune partie utile la recherche, lajout ou la suppression dun lment dans la table. On trouve souvent dans la littrature franaise la traduction littrale tables de hachage pour les dsigner. Nous prfrons employer le terme table dadressage dispers qui nous semble plus vocateur du principe gnral de la mthode.

280

Chapitre 20 Tables

0 1 2 3 4 5 6 7 8 9 10 Monique Louise Jacques Paul Pierre Germaine Maud

F IG . 20.20 Une table dadressage dispers.

sajoute celui de la vrication de la prsence ou labsence de llment recherch la place calcule. Ces mthodes sont trs souvent utilises par les compilateurs pour implanter les tables de symboles, ou par les correcteurs orthographiques des logiciels de traitement de texte. Elles sont trs performantes condition, toutefois, de bien choisir la fonction dadressage, la taille de la table et la faon de traiter les collisions.

20.7.1

Le problme des collisions

Bien souvent la fonction dadressage est une surjection, cest--dire que pour deux cls diffrentes, la fonction h renvoie une mme place. On dit quil y a une collision lorsque c1 , c2 Cl , c1 = c2 et h(c1 ) = h(c2 ) e Les collisions sont en gnral invitables8 , et nous verrons la section 20.7.3 la faon de les rsoudre. [FGS90] montre en particulier, sous certaines hypothses probabilistiques, que la fonction h disperse uniformment sur lensemble des places de la table, et que la probabilit que la fonction h renvoie la mme place pour cinq cls diffrentes avoisine zro. Il en rsulte que dune faon gnrale la recherche dun lment dans une table dadressage dispers rclame au plus cinq accs quelle que soit la taille de la table. Des rsultats statistiques tablis sur divers ensembles de cls et plusieurs fonctions dadressage semblent conrmer de faon exprimentale cette probabilit thorique [ASU89].
8 Le clbre paradoxe du jour anniversaire afrme que si plus de vingt-trois personnes sont runies dans une mme salle, il y a prs dune chance sur deux que deux dentre elles soient nes le mme jour.

20.7

Tables dadressage dispers

281

Le choix de la fonction h est donc primordial. Il doit tre tel quil minimise le nombre de collisions. Ce choix peut tre difcile faire, surtout si lon na pas de connaissance a priori sur les valeurs des cls. Sil est mauvais, il peut rendre catastrophique une mthode efcace.

20.7.2

Choix de la fonction dadressage

Une bonne fonction dadressage doit la fois rpartir les cls le plus uniformment sur lensemble des places de la table et tre simple et rapide calculer. Toute fonction dadressage doit dabord passer une reprsentation entire de la cl (sauf si la cl est dj sous cette forme) et nir par rendre un indice calcul modulo m pour revenir sur lintervalle [0, m1]. En gnral, la cl est une chane de caractres et il est important den extraire seulement les caractres signicatifs. En particulier, il est inutile de faire intervenir les caractres blancs lorsquils terminent les chanes. La reprsentation entire de la chane est obtenue avec des sommes arithmtiques ou, si loprateur est disponible dans le langage, des unions exclusives des caractres qui la composent. Voici quelques mthodes couramment utilises : prendre quatre caractres au centre de la chane ; prendre les trois premiers et les trois derniers caractres de la chane ; additionner les entiers obtenus en regroupant les caractres par blocs de quatre caractres ; calculer des suites de la forme h0 , hi = hi1 + ci . Pour cette dernire faon de prcder, la suite est trs simple calculer et programmer. Dailleurs, cest avec une fonction dadressage de ce type que la table de la gure 20.20 a t produite. Nous en donnons la programmation en JAVA.
int fctAdressage(String s) { int h=0; for (int i=0; i<s.length(); i++) h = 32*h+s.charAt(i); return Math.abs(h) % m; }

JAVA ne vrie pas les dpassements de capacit des oprations arithmtiques. La valeur de la variable h la n de boucle peut donc tre ngative, voil pourquoi il faut prendre sa valeur absolue. Pour les langages qui font la vrication, le calcul dans la boucle devra tre fait modulo m, ce qui rend de fait cette fonction plus coteuse calculer. Notez quen JAVA, on peut remplacer avantageusement le produit par 32 par un dcalage vers la gauche de cinq bits :
h = (h<<5) + s.charAt(i);

Il nexiste pas de fonction dadressage universelle. En fait, elle dpend de lensemble des cls utilis. Une fonction efcace pour des noms ou des prnoms ne le sera pas ncessairement pour les mots rservs dun langage de programmation, ou pour des codes de scurit sociale. Si lensemble des cls est connu, il est possible de chercher une fonction sans collision. Il existe dailleurs des gnrateurs de fonctions dadressage qui, partir dun ensemble de cls connu, fournissent des fonctions sans (autant que faire se peut) collision [Sch90]. En

282

Chapitre 20 Tables

revanche, si la nature des cls nest pas connue, on choisira une fonction simple calculer et dont on essaiera de prvoir le comportement partir dun chantillon de cls. Le choix de la valeur m est galement prendre en considration. La mthode dadressage dispers est trs efcace, mais ce gain de temps se fait au prix dune certaine perte de place mmoire par rapport aux mthodes qui ne requirent que lespace mmoire strictement ncessaire pour mmoriser les lments. En gnral, la taille de la table est xe lavance, et il faudra rserver un nombre de places suprieur au nombre dlments prvus. Si les lments occupent un espace mmoire important, il ne faudra pas les conserver tels quels dans la table (en JAVA, par exemple, les lments de la table seront des rfrences). Le taux de remplissage de la table, cest--dire le rapport n/m, avec n le nombre effectif dlments prsents dans la table, a une inuence sur la qualit de la fonction dadressage. Plus ce taux sera faible, plus le risque de collisions sera rduit, mais encore une fois au prix dune perte de place mmoire. Enn, et ce nest pas de moindre importance, pour viter une accumulation de collisions pour les cls dont la reprsentation entire serait un multiple de m, la taille de la table doit tre un nombre premier.

20.7.3

Rsolution des collisions

Il nest malheureusement pas toujours possible dviter les collisions, et lopration ajouter(t,e) devra quand mme insrer dans la table llment e, mme si sa cl entre en collision avec celle dun autre lment dj prsent dans la table. Il existe deux grandes techniques pour rsoudre les collisions. La premire utilise des fonctions secondaires, la seconde chane les collisions dans la table ou hors de la table. Utilisation de fonctions secondaires (adressage ouvert) Cette mthode consiste appliquer, en cas de collision, une seconde fonction dadressage pour obtenir une nouvelle place dans la table. Si cette nouvelle place provoque nouveau une collision, on applique une troisime fonction dadressage, et ainsi de suite jusqu ce quil ny ait plus de collision ou que le nombre de fonctions secondaires ait t puis. Plus formellement, cette mthode dispose dun ensemble de fonctions hi tel que : hi (c) = (h(c) + f (i)) mod m, avec f (0) = 0 La fonction f est la mthode de rsolution des collisions. La premire fonction h0 applique la cl est donc h, on lappelle la fonction dadressage primaire. Les autres fonctions hi sont appeles secondaires. Pour une cl donne, les fonctions secondaires fournissent une suite particulire de places, parmi toutes les suites possibles, cest--dire m!. Comme pour la fonction h, le choix de la fonction f est dlicat. Le cot dvaluation des fonctions secondaires doit rester faible. Dautre part, elles doivent disperser les lments le plus uniformment possible sur toutes les places disponibles de la table. Les suites de places retournes par les fonctions hi pour deux cls diffrentes doivent tre si possible diffrentes. Ce qui distinguera les diffrentes fonctions f que nous allons prsenter, cest bien sr leur pouvoir de dispersion des lments dans la table.

20.7

Tables dadressage dispers

283

Une premire mthode, appele linaire, dnit f (i) = i, cest--dire que les fonctions secondaires sont telles que : hi (c) = (h(c) + i) mod m

Cette mthode est particulirement simple, puisquelle consiste consulter h(c)+1, h(c)+ 2, h(c) + 3, ..., cest--dire les places de distance i par rapport h(c). Lalgorithme choue et sarrte lorsque i = m puisque toutes les places de la table auront t consultes en vain. Cette mthode nest toutefois pas trs bonne car les squences de places calcules pour deux cls diffrentes sont identiques, et elle provoque des accumulations dlments autour des cls qui ont t places du premier coup, sans collision. [Knu73] donne une approximation du taux de collision pour cette mthode. Pour une recherche positive, ce taux est environ gal (1 d/2)/(1 d), o d est le taux de remplissage de la table. Une variante de cette premire mthode, appele quadratique, doit son nom au fait que la fonction f a la forme dune quation du second degr. Les fonctions secondaires sont telles que : hi (c) = (h(c) + a i2 + b i) mod m

Cette mthode vite le problme daccumulation de la mthode linaire, mais les squences de places produites pour deux cls distinctes en collision restent identiques. La qualit de cette mthode dpend du choix de a et de b. Le meilleur choix pour viter de chevaucher, autant que faire se peut, les suites de place renvoyes par dautres cls, est de prendre a et b premier avec m. Notez quil sera toujours possible dajouter un lment si la table est au plus moiti remplie. La plupart du temps, on choisit a = 1 et b = 0, car les fonctions secondaires sont alors trs simples calculer avec une relation de rcurrence qui vite llvation au carr. Nous donnons la programmation en JAVA de la mthode rechercher.
// Recherche dans une table dadressage dispers // rsolution des collisions par fonctions secondaires // mthode quadratique : i2 public lment<V,C> rechercher(C cl) throws ClNonTrouveException { if (! comp.comparable(cl)) throw new ClIncomparableException(); int d=1, h=fctPrimaire(cl); do { if (table[h]==null) throw new ClNonTrouveException(); // la place h est occupe if (comp.gal(table[h].cl(), cl)) return table[h]; // collision appeler une fonction secondaire if (d>=m) // plus de fonctions secondaires throw new ClNonTrouveException(); // passer la fonction secondaire suivante h=(h+d)%m; d+=2; } while (true); }

284

Chapitre 20 Tables

Notez que lalgorithme sarrte lorsque i2 m. Dans lhypothse o m est premier, alors seulement m/2 places auront t testes. Toutefois, ceci est assez rare en pratique et narrive que lorsque le taux de remplissage de la table est lev. Une troisime mthode, appele adressage double, consiste disposer dune seconde fonction dadressage h . La fonction f est telle que f (i) = i h (c), cest--dire que les fonctions secondaires sont de la forme : hi (c) = (h(c) + i h (c)) mod m

Cette mthode vite les accumulations et, contrairement aux deux techniques prcdentes, produit pour deux cls diffrentes en collision des suites de places distinctes. Pour un adressage le plus uniforme possible sur toute la table, h (c) doit tre diffrent de 0 et premier avec m pour tout c. K NUTH [Knu73] suggre de choisir h (c) = 1 + (c mod (m 2)), avec m et m 2 premiers. Il indique galement que le taux de collision pour une recherche positive, si les deux fonctions sont indpendantes et uniformes, est environ d1 log(1 d). Chanage externe des collisions Avec cette mthode, et contrairement la technique prcdente, on ne cherche pas dans la table une nouvelle place pour les lments dont les cls entrent en collision. Ceux-ci sont rangs dans des structures dynamiques en dehors de la table. Ces structures peuvent tre des listes ou des arbres. Le cot de cette mthode est celui de la fonction dadressage primaire, plus celui de la recherche, de lajout ou de la suppression de llment dans la structure de donnes choisie. En gnral, le nombre de collisions tant faible (pas plus de cinq pour une fonction primaire uniforme), une simple liste linaire est tout fait acceptable, et conserve la mthode dadressage dispers toute son efcacit. Notez que pour une place donne, le nombre moyen dlments chans est n/m. Cette mthode a lavantage dtre trs simple mettre en uvre, et permet une gestion plus efcace de la mmoire. En effet, si la place mmoire est limite9 , il est possible de rduire la taille de la table sans que cela pnalise trop le taux de collision. Nanmoins, si pour une raison ou une autre, la fonction dadressage provoquait un nombre de collisions anormal sur une mme place dans la table, la mthode dadressage dispers perdrait alors tout son intrt. Plus encore quavec lutilisation de fonctions secondaires, il est important dvaluer sur un chantillon de cls la dispersion de la fonction primaire utilise. Dans la mthode ajouter donne ci-dessous, le chanage externe est fait laide dune liste non ordonne.
public void ajouter(lment<V,C> e) { if (!comp.comparable(e.cl())) throw new ClIncomparableException(); int h=fctPrimaire(e.cl()); if (table[h]==null) // la place est libre crer une nouvelle liste table[h]=new ListeNonOrdonneChane<V,C>(comp); // ajouter e dans la liste table[h] table[h].ajouter(e); }
9 La taille des mmoires des ordinateurs actuels ne cessent daugmenter, mais la taille des donnes manipules aussi !

20.7

Tables dadressage dispers

285

Chanage interne des collisions Le chanage des collisions se fait dans la table elle-mme. On peut dnir une zone dadressage primaire et une zone secondaire pour le chanage des collisions. Le choix de la taille des deux zones est la principale difcult de cette mthode. Une autre solution est de navoir quune zone unique, mais alors, comme pour ladressage ouvert, il peut y avoir cration de collisions secondaires qui nont pas lieu dtre. La gure 20.21 page 285 montre ces deux formes dorganisation de la table : (a) deux zones, (b) une seule zone. Une fonction dadressage primaire a calcul les indices 1 et 3 pour les cls Pierre et Maud. Les cls Louise et Jacques entrent toutes les deux en collision avec Maud.
0 1 2 3 Maud zone primaire Pierre 0 1 2 3 Maud Pierre

m1 m m+1 Jacques Louise zone secondaire m2 m1 Louise Jacques

m+p (a)
F IG . 20.21 Chanage interne.

(b)

Notez quavec cette mthode, il est ncessaire de maintenir une liste des places libres utiliser pour le chanage des collisions. Dans la gure 20.21 (a), la zone secondaire comporte p + 1 places libres. Elle commence la place m et progresse vers la place m + p. Dans la gure 20.21 (b), les lments en collision sont chans partir de la place m 1 vers le dbut de la table. Par simplication, la liste des places libres na pas t reprsente dans la gure. Comparaisons des mthodes Lorsque le taux de remplissage est relativement faible, le nombre de collisions primaires est trs faible et toutes ces mthodes de rsolution des collisions sont quivalentes. Mais lorsque ce taux est important et que le nombre de collisions augmente, le chanage dans des structures externes est prfrable. Ladressage ouvert prsente plusieurs inconvnients. Dune part, il peut attribuer une place un lment, aprs la rsolution de sa collision, qui peut trs bien tre celle dun prochain lment calcule par la fonction primaire, et qui aurait donc t libre sans la collision prcdente. Dautre part, les oprations de suppression dlments sont difciles mettre en uvre. La suppression dun lment qui provoque une collision

286

Chapitre 20 Tables

ncessite le souvenir de la collision ou le dplacement des lments qui sont en collision avec llment supprimer. Au contraire, dans le cas dun chanage externe, lopration de suppression est celle de llment dans la structure externe. La mthode de chanage externe possde galement lavantage de permettre la conservation dun nombre dlments suprieur m. En revanche, elle ncessite plus de place mmoire, ne serait-ce que celle utilise pour le chanage.

20.8

EXERCICES

Exercice 20.1. Rcrivez de faon rcursive les mthodes de recherche linaire dans une liste non ordonne et ordonne. Servez-vous de la description axiomatique du type abstrait. Exercice 20.2. Rdigez les algorithmes qui mettent en uvre les deux techniques de recherche auto-adaptative (donne la page 248) dans une liste linaire non ordonne. Exercice 20.3. On dcide de ranger les mots rservs dun langage de programmation dans une table tmr . Ces mots sont rangs par ordre de longueurs croissantes et par ordre alphabtique pour les mots dune mme longueur. Laccs cette table se fait par une autre table, appele tdl , telle que i [1, longueur(tdl)] tous les mots de longueur i sont sur lintervalle [i`me(tmr , i`me(tdl , i)), i`me(tmr , i`me(tdl , i + 1) 1)] e e e e De plus, si i`me(tdl , i) = i`me(tdl , i + 1) alors le nombre de mots de longueur i est gal e e 0. Donnez lalgorithme dune recherche dun mot rserv. Quelle en ait sa complexit ? Puis, crivez un algorithme qui construit la table des longueurs partir de la table des mots rservs. Exercice 20.4. Donnez une implantation de linterface Table avec une liste non ordonne reprsente par un tableau, puis par une structure chane. Exercice 20.5. Refaites lexercice prcdent avec une liste ordonne. Exercice 20.6. Une recherche dichotomique peut tre utilise pour rechercher la place dun lment insrer dans une table ordonne. Modiez les algorithmes de recherche dichotomique, an quil renvoie le rang de llment insrer. Exercice 20.7. La recherche dichotomique coupe en deux parties gales lespace de recherche sans se proccuper de la nature de la cl recherche. Lorsque vous recherchez un mot dans un dictionnaire, vous louvrez vers le dbut, le milieu ou la n selon que le mot recherch commence par une lettre proche du dbut, du milieu ou de la n de lalphabet. La mthode de recherche par interpolation met en uvre cette technique et amliore la mthode de recherche dichotomique. Lide est destimer lendroit o la cl pourrait se trouver sur la connaissance des valeurs disponibles. La gure 20.22 montre comment calculer le rang estim inter entre les rangs gauche et droit dune liste l. Notez que llment recherch doit ncessairement appartenir lintervalle [i`me(l, 1)), i`me(l, longueur(l))]. e e crivez un algorithme de recherche par interpolation. Comparez cette mthode avec la recherche dichotomique.

20.8

Exercices

287

cl(ime(l,droit))

c = cl(ime(l,inter)) ? cl(ime(l,gauche))

gauche inter droit

F IG . 20.22 Le rang inter est gal gauche +.

Exercice 20.8. crivez de faon itrative lalgorithme de recherche dans un arbre binaire ordonn. Exercice 20.9. Lopration de coupure divise un arbre binaire ordonn en deux arbres binaires ordonns distincts par rapport une cl c. Le premier arbre est tel que toutes les cls de ses nuds sont infrieures ou gales la cl c, le second arbre est tel que les cls de ses nuds sont strictement suprieures c. La gure 20.23 donne un exemple de coupure.
6 8 6 9

=
13 3

+
10

13

15

10

15

F IG . 20.23 Coupure dun arbre par rapport la cl 8.

Donnez la signature de cette opration. crivez les axiomes qui donnent formellement la smantique de cette fonction, puis programmez-la en JAVA. Exercice 20.10. Lopration ajouter dans un arbre binaire ordonn (donne page 253) ajoute un lment en feuille. Utilisez lopration coupure de lexercice prcdent pour dnir une opration dajout la racine. Vous crirez les axiomes de cette opration, et vous la programmerez en JAVA. Quelle est sa complexit dans le pire des cas ? Exercice 20.11. crivez un programme qui compte le nombre doccurrences des mots lus sur lentre standard. Le programme afchera la liste des mots lus en ordre alphabtique avec, pour chaque mot, son nombre doccurrences. Pour mmoriser les mots et leur nombre doccurrences, vous utiliserez un arbre binaire ordonn.

288

Chapitre 20 Tables

Exercice 20.12. On dnit un arbre quaternaire ordonn, le type abstrait qui permet de ranger des valeurs appartenant un espace deux dimensions o chaque dimension est munie dune relation dordre. Chaque nud est le pre de quatre sous-arbres qui reprsentent une partition de lespace deux dimensions, auquel appartient la valeur du nud, en quatre quadrants. Si nous appelons les deux dimensions respectivement latitude et longitude, nous pourrons appeler les quatre quadrants, respectivement, sud-ouest (SO), nord-ouest (NO), sudest (SE) et nord-est (NE). Ainsi, tant donne une valeur particulire qui occupe un nud de larbre quaternaire, son sous-arbre sud-est ne comprend que des valeurs dont la latitude est infrieure ou gale la sienne et la longitude strictement suprieure la sienne. Remarquez la dissymtrie des relations, ncessaire pour que lon puisse placer sans hsitation des points distincts mais qui ont une coordonne en commun. Dnissez les axiomes de la fonction rechercher qui recherche dans un arbre quaternaire ordonn un lment reprsent par sa cl. Celle-ci est dnie par ses deux coordonnes de longitude et de latitude. Exercice 20.13. crivez les mthodes ajouter et supprimer pour une table reprsente par un arbre 2-3-4. Un conseil : crivez la mthode supprimer de faon itrative, et pensez mmoriser tous les nuds traverss, pendant la recherche de la cl supprimer. Exercice 20.14. Modiez la mthode rechercher dans une table implante laide dun arbre 2-3-4, an quelle renvoie tous les lments de mme cl. Exercice 20.15. Donnez lalgorithme de la suppression dun lment dans une table dadressage dispers qui utilise des fonctions secondaires, dune part, dans le cas de la mthode linaire, dautre part dans le cas de la mthode quadratique. Exercice 20.16. Lorsque le taux de remplissage dune table dadressage dispers ouvert devient trop important, un prochain ajout peut considrer la table pleine. Il est alors possible de rorganiser (en anglais rehashing) les lments dans une nouvelle table plus grande (e.g. deux fois la taille de la table initiale). Modiez lalgorithme de lopration ajouter pour mettre en uvre cette technique lorsque le taux de remplissage dpasse une certaine valeur. Exercice 20.17. Les correcteurs orthographiques utilisent des tables dadressage dispers pour conserver les mots dun dictionnaire. Une technique utilise pour rduire lencombrement en mmoire du dictionnaire est de ne conserver quune table de bits, initialise 0, et telle que pour tout mot m du dictionnaire, on calcule t[h(m)] 1. Combien dlments doit possder cette table ? Quelle est la rduction de place en mmoire obtenue ? Si pour un mot m quelconque, t[h(m )] = 0, que peut-on en dduire ? Et que peut-on dduire si t[h(m )] = 1 ? Quelle est la probabilit derreur pour une table de longueur n ? Programmez un petit correcteur orthographique avec cette mthode. Il est possible damliorer la mthode en calculant plusieurs fonctions dadressage indpendantes. On teste la prsence dun mot dans la table en vriant que tous les t[hi (m )] sont gaux 1.

Chapitre 21

Files avec priorit

Les les avec priorit remettent en question le modle FIFO des les ordinaires prsentes au chapitre 17. Avec ces les, lordre darrive des lments nest plus respect. Les lments sont munis dune priorit et ceux qui possdent les priorits les plus fortes sont traits en premier. Les systmes dexploitation utilisent frquemment les les avec priorit, par exemple, pour grer laccs des travaux dimpression une imprimante, ou encore laccs des processus au processeur. Dans ce chapitre, aprs avoir dcrit le type abstrait des les avec priorit, nous prsenterons deux mises en uvre possibles : une premire laide de liste, et une seconde, trs efcace, laide de tas.

21.1

DFINITION ABSTRAITE

Le type abstrait FilePriorit dnit lensemble des les avec priorit. Les lments e de ces les appartiennent lensemble E et sont munis dune priorit prise dans Priorit . e Lensemble Priorit est pourvu dune relation dordre total qui permet dordonner les le ments du plus prioritaire au moins prioritaire. Les oprations suivantes sont dnies sur le type abstrait FilePriorit : e est-vide? : FilePriorit e ajouter : FilePriorit E Priorit e e premier : FilePriorit e supprimer : FilePriorit e boolen FilePriorit e E FilePriorit e

Lopration est-vide ? teste si la le est vide ou pas, lopration ajouter insre dans la le un nouvel lment avec sa priorit, lopration premier renvoie llment le plus prioritaire de

290

Chapitre 21 Files avec priorit

la le, et enn lopration supprimer retire llment le plus prioritaire de la le. Les axiomes du type abstrait sont laisss en exercice.

21.2

REPRSENTATION AVEC UNE LISTE

Une reprsentation qui vient en premier lesprit pour le type abstrait FilePriorit est e la structure de liste. Les listes permettent une mise en uvre trs simple de ce type abstrait. Dans cette section, nous ne nous intresserons qu la complexit des oprations du type abstrait, selon que la liste est ordonne ou non. Lcriture des algorithmes et la programmation en JAVA de ces oprations ne posent gure de difcults et nous les laisserons en exercice. Lajout dun nouvel lment dans une liste non ordonne se fait en tte de liste. Le cot de lopration est efcace, il est en O(1). En revanche, comme les lments sont dans un ordre quelconque, les oprations premier et supprimer ncessitent une recherche linaire de llment le plus prioritaire. Cette recherche peut demander un parcours complet de la liste, et la complexit de ces deux oprations est O(n). Lorsque le type abstrait FilePriorit est implant laide une liste ordonne, les ope rations premier et supprimer sont en O(1), puisquon fera en sorte que les lments de la liste soient placs par ordre de priorit dcroissante. Lopration ajouter doit maintenir lordre sur les lments de la liste, et sa complexit est celle dune recherche dans une liste ordonne, cest--dire O(n). Le choix de la reprsentation dune le avec priorit sera dicte par la nature des oprations utilises. Sil y a peu dajouts et beaucoup de consultations du premier de la le, on prfrera une liste ordonne. En revanche, sil y a de nombreux ajouts, en alternance avec des suppressions qui maintiennent une le de petite taille, une liste non ordonne sera trs efcace. Quoi quil en soit, la reprsentation du type abstrait FilePriorit laide de listes lie naires nest pas trs efcace, surtout si les les possdent un nombre dlments signicatif, cest--dire au-del dune centaine dlments. Pour les les de grande taille, il existe une reprsentation trs efcace, appele tas, que nous allons dcrire maintenant.

21.3

REPRSENTATION AVEC UN TAS

Un tas est un arbre binaire parfait partiellement ordonn. Rappelons quun arbre parfait est un arbre dont toutes les feuilles sont situes sur au plus deux niveaux. Les feuilles du dernier niveau sont places le plus gauche (voir le chapitre 19 page 233). La relation dordre que dnit un arbre partiellement ordonn sur ses lments est telle que la valeur de la racine est suprieure ou gale la valeur des nuds de ses sous-arbres gauche et droit. La valeur la plus grande dun tel arbre est toujours situe la racine. Notez que la relation ordre aurait pu tout aussi bien tre inverse, de telle faon que la valeur la plus petite soit la racine. La gure 21.1 montre un exemple darbre partiellement ordonn. Un tas runit la fois les proprits dun arbre binaire parfait, et dun arbre partiellement ordonn. La gure 21.2 montre larbre de la gure 21.1 organis en tas.

21.3

Reprsentation avec un tas

291

38

25

30

12

13

1
F IG . 21.1 Un arbre partiellement ordonn.

38

25

30

12

13

F IG . 21.2 Larbre de la gure 21.1 sous forme de tas.

Un tas offre une reprsentation trs efcace du type abstrait FilePriorit , puisquavec e une telle structure, la complexit des oprations est O(1) ou O(log2 n). Par la suite, et pour simplier, seules les valeurs des priorits apparatront dans les gures. Ces priorits sont des entiers, et plus lentier sera grand, plus la priorit sera forte.

21.3.1

Premier

Puisque llment le plus prioritaire est toujours plac la racine de larbre, laccs sa valeur est trs efcace, et sera toujours en O(1).
Algorithme premier(t) {Rle : renvoie llment le plus prioritaire du tas t} rendre racine(t)

292

Chapitre 21 Files avec priorit

Nous allons voir maintenant que les oprations dajout et de suppression dlments, qui demandent une rorganisation du tas, sont elles aussi performantes. Elles ne ncessitent que le parcours dune branche de larbre binaire. Comme larbre est parfait, la hauteur dune branche est gale au plus log2 n , o n est le nombre dlments dans le tas. La complexit des oprations dajout et de suppression est, au pire, gale O(log2 n).

21.3.2

Ajouter

Lopration dajout consiste, dans un premier temps, placer le nouvel lment et sa priorit en feuille, celle qui suit la dernire feuille de larbre. Le nouvel lment nest pas ncessairement correctement plac et lordre partiel sur les lments du tas nest peut-tre plus respect. Il faut alors rordonner le tas, et chercher une bonne place pour le nouvel lment.
38 27

25

30 25

12

13

27

13 38

27

30

12

25

13

F IG . 21.3 Ajout dun lment de priorit 27.

Algorithme ajouter(t, e, p) a cons((e,p),,) mettre a aprs la dernire feuille du tas t rordonner-tas1(t,a)

21.3

Reprsentation avec un tas

293

Lalgorithme de rordonnancement du tas procde par changes successifs de la valeur du nouvel lment avec la valeur de ses pres sur la branche qui remonte jusqu la racine du tas. Tant que sa valeur de priorit est suprieure celle du pre, on fait lchange. Lalgorithme sarrte quand un pre possde une valeur de priorit suprieure ou gale, ou bien lorsque la racine du tas est atteinte. Dans ce dernier cas, le nouvel lment est plac la racine du tas. La gure 21.3 page 292 montre lajout dun lment de priorit 27. Aprs avoir t plac en feuille, llment est successivement chang avec les lments de priorit 13 et 25. Sa position nale est celle du ls gauche de la racine. Lalgorithme de rordonnancement est dcrit rcursivement comme suit :
Algorithme rordonner-tas1(t,a) si a=t alors si priorit(racine(a)) > priorit(racine(pre(a))) alors changer(valeur(racine(a)),valeur(racine(pre(a)))) rordonner-tas1(t,pre(a)) finsi finsi

21.3.3

Supprimer

La suppression de llment le plus prioritaire du tas consiste remplacer la valeur de la racine du tas par la valeur de la dernire feuille du tas, puis supprimer cette feuille, et enn rordonner le tas.
Algorithme supprimer(t) racine(t) racine(dernire feuille du tas t) supprimer la dernire feuille de t rordonner-tas2(t)

Comme prcdemment, la rorganisation du tas procde par changes successifs, mais en partant, cette fois-ci, de la racine et en descendant vers les feuilles. Si elle lui est infrieure, la valeur dplace est change avec la valeur maximale des priorits de ses ls gauche ou droit. La gure 21.4 page 294 montre la suppression de llment le plus prioritaire du tas, i.e. 38. Llment de priorit 13 est supprim et copi la racine du tas, et sa feuille est supprime. Il est ensuite chang avec llment de priorit 30.
Algorithme rordonner-tas2(a) si a nest pas une feuille alors {chercher le fils qui possde la valeur min} si sad(a) ou priorit(racine(sag(a))>priorit(racine(sad(a))) alors {un seul fils gauche ou le fils gauche a la priorit maximale} fils-max sag(a) sinon {le fils droit a la priorit maximale} fils-max sad(a) finsi

294

Chapitre 21 Files avec priorit

30 13 13 27 30

12

25

13

30

27

13

12

25

F IG . 21.4 Suppression dun lment le plus prioritaire.

{changer la valeur de a avec fils-min si ncessaire} si priorit(racine(a))<priorit(racine(fils-max)) alors {racine(i) a la priorit maximale} changer(valeur(racine(a)),valeur(racine(fils-max))) rordonner-tas2(fils-max) finsi finsi

21.3.4

Limplantation en Java

Les signatures des oprations du type abstrait FilePriorit sont donnes par linterface e gnrique FilePriorit :
/** V ensemble des valeurs de la file avec priorit * P ensemble des priorits */ public interface FilePriorit<V,P> { public boolean estVide();

21.3

Reprsentation avec un tas

295

public V premier() throws FileVideException; public void supprimer() throws FileVideException; public void ajouter(V e, P p); }

Lutilisation de tableaux pour implanter les tas est particulirement efcace car laccs aux nuds de larbre est direct. De plus, nous lavons dj vu, le pre dindice i est li ses ls gauche et droit dindice, respectivement, 2i et 2i + 1. Et le pre dun nud dindice i est lindice i/2. Par exemple, dans la gure 21.5, le nud de valeur 25 est lindice 2, et possde deux ls 12 et 13 aux indices 2 2 = 4 et 2 2 + 1 = 5. Le pre du nud de valeur 1 et dindice 9, est lindice 9/2 = 4.
38
1

25
2

30 12 13
3 4 5

9
6

1
7

5
8

1
9 ...

F IG . 21.5 Le tas de la gure 21.2 dans un tableau.

On dnit la classe gnrique TasListeTableau qui implante linterface FilePriorit, et qui tend la classe ListeTableau reprsentant les listes mises en uvre laide dun tableau. Larithmtique sur les indices est alors faite sur les rangs des lments dans la liste. Remarquez que nous aurions pu tendre nimporte quelle autre classe dimplantation du type abstrait Liste, mais pour des raisons defcacit en termes daccs aux lments de la liste, seule la classe ListeTableau convient. Lattribut cmp conserve les oprations de comparaisons ncessaires pour tablir lordre partiel sur les lments du tas en fonction de leurs priorits. Les valeurs des lments de la le et leurs priorits seront conserves dans des objets de type lment, dni la page 245. Lattribut cl reprsente la priorit de llment. Llment qui possde la cl de valeur la plus grande possde la priorit la plus forte.
public class TasListeTableau<V,P> extends ListeTableau<lment<V,P>> implements FilePriorit<V,P> { protected Comparateur<P> cmp; ... }

An daccrotre la lisibilit des mthodes qui implantent les oprations du type abstrait FilePriorit , nous dnissons les mthodes prives suivantes, qui renvoient, respectivee ment, la valeur (de type lment) du nud courant, du nud de son pre et de ses ls gauche et droit.
// Rle : renvoie la valeur du nud dindice i private lment<V,P> racine(int i) { return ime(i); } // Rle : renvoie la valeur du nud du pre du nud dindice i private lment<V,P> pre(int i) { return ime(i/2); }

296

Chapitre 21 Files avec priorit

// Rle : renvoie la valeur du nud du sag du nud dindice i private lment<V,P> sag(int i) { return ime(2*i); } // Rle : renvoie la valeur du nud du sad du nud dindice i private lment<V,P> sad(int i) { return ime(2*i+1); }

La mthode premier renvoie la valeur de la racine du tas. La racine est au premier rang de la liste.
public V premier() throws FileVideException { if (estVide()) throw new FileVideException(); return racine(1).valeur(); }

Le nouvel lment est ajout en feuille lextrmit du tas. Son rang dans la liste est gal
longueur()+1. Lajout de la nouvelle feuille dans la liste est fait par la mthode ajouter de la super classe ListeTableau. Lalgorithme rcursif de rordonnancement donn plus

haut scrit simplement et efcacement de faon itrative.


public void ajouter(V e, P p) { int i=longueur(); // mettre le nouvel lment la dernire place ajouter(++i, new lment(e,p)); // rordonnancement du tas while (i>1 && cmp.suprieur(racine(i).cl(), pre(i).cl())) changer(i, i/=2); }

Pour supprimer llment prioritaire de la le, on affecte au nud de la racine, la valeur du nud de rang longueur(), et on supprime ce dernier lment de la liste. Le rordonnancement du tas scrit galement de faon itrative, et traite, en partant de la racine, les nuds qui ne sont pas des feuilles (cest--dire les nuds de rang 1 longueur()/2 jusqu ce que la position nale soit trouve.
public void supprimer() throws FileVideException { if (estVide()) throw new FileVideException(); // la file nest pas vide changer la valeur de la racine affecter(1,ime(longueur())); // supprimer la dernire feuille super.supprimer(longueur()); int i=1, lg=longueur(); // rordonnancement du tas while (i<=lg/2) { // le nud i possde au moins un fils int filsMax = (2*i == lg || cmp.suprieur(sag(i).cl(),sad(i).cl())) ? // un seul fils gauche ou le fils gauche a la priorit maximale

21.4

Exercices

297

2*i // le fils droit a la priorit maximale : 2*i+1; if (cmp.infrieur(racine(i).cl(), racine(filsMax).cl())) { // racine(i) a la priorit maximale changer(i,filsMax); i = filsMax; } else // racine(i) est sa position finale return; } // racine(i) est sa position finale } // fin supprimer

21.4

EXERCICES

Exercice 21.1. Proposez un algorithme en O(n log2 n) qui trouve la k e plus grande valeur dune suite quelconque dentiers lus sur lentre standard. Exercice 21.2. Proposez un algorithme de recherche dun lment dans un tas. Quelle est sa complexit ? Est-ce quun tas est adapt ce type dopration ? Exercice 21.3. Programmez une implantation de linterface FilePriorit laide dun tas reprsent par une structure chane. Exercice 21.4. Une gnralisation des tas consiste les reprsenter par des arbres quelconques. Larbre est toujours partiellement ordonn, mais le nombre de ls de chaque nud nest plus limit deux. Donnez les algorithmes des oprations ajouter et supprimer pour un tas dont le nombre ls de chaque nud est born par m. Indiquez la complexit de ces oprations, puis comparez les avantages et les inconvnients dun tas binaire et dun tas dordre m.

Chapitre 22

Algorithmes de tri

22.1

INTRODUCTION

Un tri consiste ordonner de faon croissante (ou dcroissante) une suite dlments partir des cls qui leur sont associes. Pour une suite de n lments, il sagit donc de trouver une permutation particulire des lments parmi les n! permutations possibles. Les mthodes de tri sont connues depuis fort longtemps, et leurs algorithmes ont t tudis en dtail, notamment dans [Knu73]. Prenons, par exemple, la suite1 dentiers ordonner suivante :
53 914 230 785 121 350 567 631 11 827 180

Une opration de tri ordonnera les lments de faon croissante et renverra la suite :
11 53 121 180 230 350 567 631 785 827 914

On distingue les tris internes des tris externes. Un tri est dit interne, si lensemble des cls trier rside en mmoire principale. Les lments trier sont gnralement placs dans des tableaux. Au contraire, un tri est externe lorsque lensemble des cls ne peut rsider en mmoire centrale, et doit tre conserv dans des chiers. Ces tris utilisent des mthodes dinterclassement et cherchent minimiser le nombre de chiers auxiliaires utiliss. Dans lexemple prcdent, la cl est une simple valeur entire. Toutefois, les cls peuvent tre structures, et possder plusieurs niveaux. On parle alors de cls primaire, secondaire, etc. Dans un annuaire tlphonique, les abonns sont classs par ordre alphabtique dabord sur les noms de famille (la cl primaire), puis en cas dhomonymie, par ordre alphabtique
1 Dans tout ce chapitre, les algorithmes prsents seront appliqus sur cette suite de rfrence, et pour simplier nous assimilerons la valeur de llment sa cl.

300

Chapitre 22 Algorithmes de tri

sur les prnoms (la cl secondaire). La complexit de la cl ne modie pas les algorithmes de tri, seule la mthode de comparaison des cls change.

22.2
trier

TRIS INTERNES
: Liste Liste

Une opration de tri interne est dnie par la signature suivante :

Nous considrerons que les lments de la liste l renvoye par lopration de tri sont en ordre croissant tel que : i, j [1, longueur(l)], i < j cl(i`me(l, i)) e e cl(i`me(l, j)) e e

Un tri peut se faire sur place, cest--dire quune seule liste est utilise, ou avec recopie, il a alors recours une ou plusieurs listes auxiliaires (en gnral une seule). Dans cette section, nous ne prsenterons que des mthodes de tri sur place par comparaison de cls. La complexit temporelle des algorithmes de tri sexprime en nombre de comparaisons des cls. Selon lordre initial des cls dans la liste, la complexit dun tri pourra varier. Nous distinguerons la complexit moyenne, la meilleure et la pire. En gnral, on se rfre la complexit moyenne pour tablir les performances dun tri. Il est dmontr [Knu73] que la complexit la pire dun tri sur place par comparaison de cls ne peut tre infrieure O(n log2 n). Le nombre de comparaisons nest toutefois pas sufsant pour dcrire totalement lefcacit dun tri. La complexit en terme de nombre de dplacements des lments a une incidence non ngligeable sur les performances des tris. Le cot dune opration daffectation dlment, surtout si sa taille est grande, peut tre suprieur celui dune opration de comparaison. Selon les mthodes de tris, nous valuerons les dplacements ou les changes. Les tris internes peuvent tre classs en deux catgories selon leur complexit. Dune part, les tris simples dont la complexit moyenne en comparaisons est quadratique, et qui ont des performances mdiocres. Dautre part, des tris labors dont les algorithmes sont plus complexes, mais plus performants puisque leur complexit moyenne en comparaisons est logarithmique. Une seconde classication possible peut tre faite selon la mthode de tri utilise. Dans cette section, nous prsenterons trois catgories de mthode de tri interne sur place : par slection, par insertion, et par changes. Pour chacune de ces catgories, nous donnerons un tri simple et un tri labor.

22.2.1

Limplantation en Java

Limplantation des algorithmes de tri utilisera le type Liste donn au chapitre 17, complt par les mthodes affecter et changer. Ces oprations sont dnies par les signatures et les axiomes suivants : aecter : Liste entier E Liste changer : Liste entier entier Liste e

22.2

Tris internes

301

l Liste, i, j [1, longueur(l)], e E (1) longueur(changer(l, i, j)) = longueur(l) e (2) i`me(changer(l, i, j), i) = i`me(l, j) e e e (3) i`me(changer(l, i, j), j) = i`me(l, i) e e e (4) longueur(aecter(l, i, e)) = longueur(l) (5) r [1, longueur(l)] et r = i, i`me(aecter(l, i, e), r) = i`me(l, r) e e (6) r = i, i`me(aecter(l, i, e), r) = e e An que ces algorithmes de tri soient efcaces, limplantation de la liste devra employer un tableau pour un accs direct aux lments de la liste. Les lments trier sont de type lment dni la page 245. Les oprations de comparaisons spciques au type des cls des lments sont passes en paramtre de la mthode de tri. Ainsi, nous dnirons une classe gnrique TrieurDeListes, hritire de ListeTableau, paramtre sur le type des valeurs et des cls trier. Cette classe et les en-ttes des mthodes de tri auront la forme suivante :
public class TrieurDeListes<V,C> extends ListeTableau<lment<V,C>> { public void triXXX(Comparateur<C> c) { } }

22.2.2

Mthodes par slection

Le principe des mthodes de tri par slection est de rechercher le minimum de la liste, de le placer en tte et de recommencer sur le reste de la liste. la ie tape, la sous-liste forme des lments du rang 1 au rang i 1 est trie, et tous les lments du rang i au rang n possdent des cls suprieures ou gales celles des lments de la sous-liste dj trie. Llment de cl minimale, trouve entre le rang i et le rang n, est plac au rang i (voir la gure 22.1), et le tri se poursuit ltape i + 1.

i min

sousliste de ime(1) ime(i1) trie

sousliste de ime(i) ime(n) non trie

F IG . 22.1 Tri par slection ltape i.

Nous prsentons deux tris par slection. Le premier, le tri par slection directe, a des performances mdiocres, sa complexit est O(n2 ). Le second, le tri en tas, est particulirement efcace. Sa complexit est au pire gale O(n log2 n). Slection directe Dans cette mthode de tri, la recherche du minimum partir de llment de rang i, est une simple recherche linaire. Le droulement de cette mthode, en utilisant la suite dentiers de rfrence, se droule comme suit :

302

Chapitre 22 Algorithmes de tri

|53 11 11 11 11 11 11 11 11 11

914 230 785 121 350 567 631 11 827 |914 230 785 121 350 567 631 53 827 53 |230 785 121 350 567 631 914 827 53 121 |785 230 350 567 631 914 827 53 121 180 |230 350 567 631 914 827 53 121 180 230 |350 567 631 914 827 53 121 180 230 350 |567 631 914 827 53 121 180 230 350 567 |631 914 827 53 121 180 230 350 567 631 |914 827 53 121 180 230 350 567 631 785 |827

180 180 180 180 785 785 785 785 785 914

la ie tape, la cl courante est droite de la barre verticale, et le minimum de la sous-liste est soulign. Cet algorithme de tri scrit comme suit :
Algorithme SlectionDirecte(l) {Rle: trie la liste l en ordre croissant des cls} pourtout i de 1 longueur(l)-1 faire {Invariant : la sous-liste de ime(1) ime(i-1) est trie et k[1,i-1],k [i,longueur(l)],cl(ime(l,k)) cl(ime(l,k ))} min i {chercher lindice du min entre ime(i) et ime(l,longueur(l))} pourtout j de i+1 longueur(l) faire si cl(ime(l,j))<cl(ime(l,min)) alors min j finsi finpour {changer ime(l,i) avec ime(l,min)} si i=min alors ime(l,i) ime(l,min) finsi finpour

Pour une liste de longueur n, la premire boucle est excute n 1 fois. litration i, la boucle interne produit n i itrations. Puisque la comparaison est excute systmatiquement, le nombre de comparaisons moyen, le pire et le meilleur sont tous les trois gaux n1 1 2 2 i=1 i = 2 (n n) = O(n ). Il y a un change chaque itration, sauf dans le cas o le minimum est dj au rang i. Si on considre que la probabilit est 1/(n i) pour que le minimum soit au rang i, le tri n complet produit donc n 1 i=2 1/i n ln(n) changes, soit une complexit gale O(n). Tri en tas Le tri en tas, ou heapsort en anglais, est une mthode de tri par slection particulirement efcace puisque sa complexit est au pire gale O(n log2 n). Cette mthode organise la partie de la liste qui nest pas encore trie en tas (voir la section 21.3, la page 290). Les lments les plus prioritaires possdent les cls les plus grandes. Pour des raisons dimplmentation (nous avons vu quil est commode que la racine du tas soit au rang 1), le tas est plac en tte de liste. Au dpart, avant lopration de tri proprement

22.2

Tris internes

303

dite, lalgorithme doit procder la cration dun tas initial, partir de tous les lments de la liste. Nous dcrirons cette premire phase un peu plus loin. Voyons comment procde ce tri la ie tape. ce moment du tri, la liste est organise comme lindique la gure 22.2. Tous les lments compris entre le rang i + 1 et n sont ordonns de faon croissante et possdent des cls suprieures ou gales celles des lments du tas, compris entre le rang 1 et i. Llment de rang 1 possde la cl la plus grande du tas. n 1 i
sousliste de ime(1) ime(i) organise en tas sousliste de ime(i+1) ime(n) trie

F IG . 22.2 Tri en tas ltape i.

Le prochain lment placer au rang i est llment dont la cl est la plus grande sur lintervalle [1, i]. Sa recherche dans le tas est immdiate puisque cest le premier de la liste. La complexit de la recherche est donc gale O(1). Llment de rang i prend ensuite la place du premier lment de la liste qui est mmoris. Le tas, form des lments compris entre les rangs 1 et i 1, est alors rordonn par lalgorithme donn la page 293. Enn, llment de cl maximale mmoris est plac au rang i. Lalgorithme complet du tri en tas est le suivant :
Algorithme triEnTas(l) {Rle: trie la liste l en ordre croissant des cls} cration du tas initial {la liste l est organise en tas} pourtout i de longueur(l) 2 faire {Invariant : la sous-liste de ime(l,i+1) ime(l,longueur(l))} {est trie et k[1,i-1],k [i,longueur(l)], cl(ime(l,k)) cl(ime(l,k ))} max = ime(l, 1) ime(l,1) ime(l,i) {rordonner le tas entre 1 et i-1} rordonner-tas2(l, 1, i-1) {max est le prochain ime(l,i)} ime(l,i) max finpour

Ltape initiale de cration du tas partir de la liste de rfrence construit la liste :


914 827 567 785 180 350 230 631 11 121 53

Le droulement de la mthode de tri en tas, applique cette liste produit les dix tapes suivantes :
914 827 785 631 827 785 631 180 567 567 567 567 785 631 121 121 180 180 180 11 350 350 350 350 230 230 230 230 631 53 53 |53 11 121 11 |121 |11 827 785 827 |53 914 914 914

304

Chapitre 22 Algorithmes de tri

567 350 230 180 121 53 11

180 180 180 121 11 |11 53

350 230 53 53 |53 121 121

121 121 121 |11 180 180 180

11 11 |11 230 230 230 230

53 |230 |53 567 350 567 350 567 350 567 350 567 350 567

631 631 631 631 631 631 631

785 785 785 785 785 785 785

827 827 827 827 827 827 827

914 914 914 914 914 914 914

Pour construire le tas initial, il faut ordonner de faon partielle tous les lments de la liste. Chaque lment correspond un nud particulier dun arbre parfait. On applique lalgorithme de rordonnancement rordonner-tas2 sur chacun des nuds (qui ne sont pas des feuilles), en partant des nuds de rang longueur(l)/2 jusqu la racine, cest--dire le nud de rang 1. Pour chaque nud i, les lments compris entre les rangs i et longueur(l) sont rordonns en tas. Le tas initial est construit comme suit :
Algorithme cration du tas pourtout i de longueur(l)/2 1 faire rordonner-tas2(l, i, longueur(l) ) finpour

criture en JAVA Nous donnons, ci-dessous, lcriture complte du tri en tas en JAVA. La mthode
rordonnerTas rordonne le tas depuis le rang i jusquau rang n, en fonction de llment de rang i, Le paramtre c donne les oprations de comparaison sur les cls des lments. Les mthodes racine, sag et sad sont celles donnes la page 295.
private void rordonnerTas(int i, int n, Comparateur<C> c) while (i<=n/2) { // le nud i possde au moins un fils int filsMax = (2*i == n || c.suprieur(sag(i).cl(), sad(i).cl())) ? // un seul fils gauche ou le fils gauche // a la priorit maximale 2*i // le fils droit a la priorit maximale : 2*i+1; if (c.infrieur(racine(i).cl(),racine(filsMax).cl())) { // le nud i a la priorit maximale changer(i,filsMax); i = filsMax; } else return; } } // fin rordonnerTas

Enn, la mthode principale du tri en tas est une traduction directe de lalgorithme. Elle mmorise dans une variable temporaire max le maximum du tas situ au rang 1 de la liste. Celui-ci est remplac par la valeur de llment de rang i. Le tas est alors diminu dun

22.2

Tris internes

305

lment, et il est rordonn entre les rangs 1 et i 1. Enn, la valeur de max est affecte au rang i, sa place dnitive.
public void triEnTas(Comparateur<C> c) { // construction du tas initial for (int i=longueur()/2; i>=1; i--) rordonnerTas(i,longueur(),c); // tri de la liste for (int i=longueur(); i>1; i--) { // invariant : la sous-liste de ime(i+1) ime(longueur()) // est trie et k[1,i-1],k [i,longueur()], // cl(ime(k))<=cl(ime(k )) lment<V,C> max=ime(1); affecter(1,ime(i)); // rordonner le tas entre 1 et i-1 rordonnerTas(1,i-1,c); // max est le prochain ime(i) affecter(i,max); } }

Complexit du tri en tas La complexit du tri en tas est gale la complexit de la cration du tas initial, plus celle du tri proprement dit. Nous allons nous intresser la complexit dans le pire des cas. La complexit de la cration du tas est gale la somme des cots de rordonnancement de chacun des sous-arbres du tas. Puisque chaque lment du tas est la racine dun sous-arbre parfait, il y a n sous-arbres rordonner. On dnit le cot total du rordonnancement comme la somme des cots des rordonnancements de chacun des sous-arbres, exprim en nombre ditrations effectues dans la mthode rordonnerTas. Le cot par nud est born par la profondeur du sous-arbre dont il est la racine. Pour les nuds de rang compris entre n/2 + 1 et n, le cot est nul puisque ce sont des feuilles. Pour les nuds de rang compris entre n/4 + 1 et n/2, le cot est infrieur ou gal 1. Pour les nuds de rang compris entre n/8 + 1 et n/4, le cot est infrieur ou gal 2. Et ainsi de suite jusqu la racine du tas, o le cot est infrieur ou gal log2 (n + 1) 1. Le cot maximal est calcul pour un arbre parfait complet, cest--dire dont le dernier niveau possde toutes ses feuilles. On voit quil est gal la somme :
log2 (n+1)

S=
i=1

2i1 log2 (n + 1) i = n log2 (n + 1)

La somme S tant borne par n, la complexit de la construction du tas est au pire O(n). La phase de tri proprement dite comporte n 1 tapes. La complexit de chaque tape tant celle de la complexit dun rordonnancement du tas, sa complexit est au pire gale O(n log2 n). En ajoutant la complexit de la cration du tas initial et celle de la phase de tri, lalgorithme de tri en tas conserve une complexit, dans le pire des cas, gale O(n log2 n).

306

Chapitre 22 Algorithmes de tri

22.2.3

Mthodes par insertion

Dans ces mthodes, la ie tape du tri, les i 1 premiers lments forment une sousliste trie dans laquelle il sagit dinsrer le ie lment sa place (voir la gure 22.3 page 306). Nous prsentons trois algorithmes selon cette mthode : le tri par insertion directe et sa variante par insertion dichotomique, et le tri par distances dcroissantes.
1 j i n

sousliste de ime(1) ime(i1) trie

sousliste de ime(i) ime(n) non trie

F IG . 22.3 Tri par insertion ltape i.

Insertion squentielle La liste est parcourue partir du rang 2 jusquau dernier. ltape i, les i 1 premiers lments forment une sous-liste trie. Un rang dinsertion du ie lment est recherch de faon squentielle entre le rang i et le premier rang. Il est tel que la sous-liste reste trie. Lors de linsertion, llment de rang i est mmoris dans une variable auxiliaire et un dcalage dune place vers la droite des lments compris entre le rang j et le rang i 1 est ncessaire. Pour amliorer lefcacit de lalgorithme, ce dcalage doit tre fait pendant la recherche du rang dinsertion. Le tri par insertion appliqu la liste de rfrence produit les tapes suivantes :
53 |914 230 785 121 350 567 631 53 914 |230 785 121 350 567 631 53 230 914 |785 121 350 567 631 53 230 785 914 |121 350 567 631 53 121 230 785 914 |350 567 631 53 121 230 350 785 914 |567 631 53 121 230 350 567 785 914 |631 53 121 230 350 567 631 785 914 11 53 121 230 350 567 631 785 11 53 121 230 350 567 631 785 11 53 121 180 230 350 567 631 11 827 180 11 827 180 11 827 180 11 827 180 11 827 180 11 827 180 11 827 180 |11 827 180 914 |827 180 827 914 |180 785 827 914

Lalgorithme du tri par insertion squentielle est le suivant :


Algorithme TriInsertion(l) {Rle: trie la liste l en ordre croissant des cls} pourtout i de 2 longueur(l) faire {Invariant : la sous-liste de ime(l,1) ime(l,i-1) est trie} x ime(l,i) j i-1 sup vrai

22.2

Tris internes

307

tantque j>0 et sup faire {on dcale et on cherche le rang dinsertion} {simultanment de faon squentielle} si cl(ime(l,j)) cl(x) alors sup faux sinon {on dcale} ime(l,j+1) ime(l,j) j j-1 finsi fintantque ime(l,j+1) x finpour

Notez que loprateur logique de conjonction (&&) du langage JAVA nvalue son second oprande boolen que si le premier est vrai. Cela permet dliminer la variable boolenne dans la programmation de lalgorithme.
public void triInsertion(Comparateur<C> c) { for (int i=2; i<=longueur(); i++) { // invariant : la sous-liste de ime(1) ime(i-1) est trie lment<V,C> x=ime(i); int j=i-1; // rechercher le rang dinsertion while (j>0 && (c.suprieur(ime(j).cl(),x.cl()))) { // on dcale et on cherche le rang dinsertion // simultanment de faon squentielle affecter(j+1,ime(j)); j--; } // j+1 est ce rang dinsertion affecter(j+1,x); } }

En utilisant une sentinelle, il est galement possible de supprimer le test sur le rang infrieur de la liste (i.e. j>0) ncessaire lorsque linsertion a lieu au premier rang. En gnral, on choisit comme sentinelle llment de rang i que lon place au dbut de la liste. Si la boucle atteint la sentinelle, elle sarrtera de fait. Dans le pire des cas, cest--dire si la liste est en ordre invers, il y a i 1 comparaisons n1 chaque tape (lorsque j est gal zro, seul j>0 est valu), soit i=1 i = 1 (n2 n) 2 comparaisons. Au contraire, lorsque la liste est dj trie, le tri donne sa meilleure complexit, puisque le nombre de comparaisons est gal n1. Enn, le nombre moyen de comparaisons 1 est 4 (n2 + n 2), si les cls sont rparties de faon quiprobable. La complexit moyenne de ce tri est O(n2 ). chaque tape, le nombre de dplacements est gal au nombre de comparaisons plus un. Dans le pire des cas, il est gal 1 (n2 + n 2), dans le meilleur 2(n 1) et en moyenne 2 1 2 4 (n + 5n 6).

308

Chapitre 22 Algorithmes de tri

Insertion dichotomique Puisque la liste dans laquelle on recherche le rang dinsertion est trie, on voit quil peut tre avantageux de remplacer la recherche linaire par une recherche dichotomique. partir de lalgorithme prcdent, la programmation en JAVA de cette mthode scrit :
public void triInsertionDicho(Comparateur<C> c) { for (int i=2; i<=longueur(); i++) { // invariant : la sous-liste de ime(1) ime(i-1) est trie lment<V,C> x=ime(i); if (c.infrieur(x.cl(), ime(i-1).cl())) { // rechercher le rang dinsertion de x int gauche=1, droite=i-1; while (gauche<droite) { int milieu = (gauche+droite)/2; if (c.infrieurOugal(x.cl(), ime(milieu).cl())) droite=milieu; else // cl(x)>cl(ime(milieu)) gauche=milieu+1; } // gauche est le rang dinsertion // dcaler tous les lments de ce rang i-1 for (int j=i-1; j>=gauche; j--) affecter(j+1,ime(j)); // mettre llment cl au rang gauche affecter(gauche,x); } } }

Notez que le choix de la troisime version de la recherche dichotomique, donne la page 251, se justie dans la mesure o il y a plus de recherches ngatives que positives. Le nombre de comparaisons est nettement amlior. Nous avons vu quune recherche ngative dans une liste de longueur n demandait au plus log2 n + 1 comparaisons. chaque tape du tri, le nombre de comparaisons est gal log2 (i 1) + 2, soit dans tous les cas n au total i=2 log2 (i 1) + 2n 2 comparaisons. La complexit est gale O(n log2 n). Toutefois, cette mthode perd beaucoup de son intrt puisque linsertion ncessite toujours un dcalage linaire. La complexit de ce tri reste donc O(n2 ). Cette variante est en fait peine plus efcace que le tri par insertion squentielle. Le tri par distances dcroissantes Ce tri, conu par D.L. S HELL en 1959, est une amlioration notable du tri par insertion squentielle. Lide de ce tri est de former ltape i plusieurs sous-listes dlments distants dun nombre xe de positions. Ces sous-listes sont tries par insertion. ltape suivante, on rduit la distance et on recommence ce procd. la dernire tape, la distance doit tre gale 1. En supposant que les valeurs des distances choisies sont 5, 2 et 1, les tapes produites par cette mthode sont donnes ci-dessous.

22.2

Tris internes

309

53 53 53 11

914 567 11 53

230 230 121 121

785 11 180 180

121 121 230 230

350 180 567 350

567 914 350 567

631 631 631 631

11 785 785 785

827 827 827 827

180 350 914 914

la premire tape, lalgorithme trie sparment les cinq listes suivantes formes dlments distants de cinq positions :
53 914 230 785 121 350 567 631 11 827 180

seconde tape, les lments distants de deux positions forment les deux listes suivantes, qui sont tries :
53 567 230 11 121 180 914 631 785 827 350

Enn, la troisime tape tous les lments forment la liste suivante pour un dernier tri.
53 11 121 180 230 567 350 631 785 827 914

Il est vident que cette mthode nit par trier la liste puisque dans le pire des cas tout le travail est fait par la dernire passe car la distance sparant deux cls est gale 1, comme dans le cas dun tri par insertion squentielle classique. Mais alors quel est lavantage de cette mthode par rapport celle du tri par insertion squentielle ? Nous avons remarqu que pour une liste trie, le nombre de comparaisons pour un tri par insertion est gal n 1. Ainsi chaque tape, le tri courant protera des tris des tapes prcdentes. Ceci tient compte du fait quune sous-liste trie ltape i reste trie aux tapes suivantes.
Algorithme TriShell(l) {Rle: trie la liste l en ordre croissant des cls} distance longueur(l) div 2 tantque distance>0 faire {trier par insertion les lments spars de "distance"} pourtout i de distance+1 longueur(l) faire x ime(l,i) j i-distance sup vrai {rechercher la place de x dans la sous-liste ordonne} {et dcaler simultanment} tantque j>0 et sup faire si cl(ime(l,j)) cl(x) alors sup faux sinon ime(l,j+distance) ime(l,j) j j-distance finsi {insrer x sa place} ime(l,j+distance) x

310

Chapitre 22 Algorithmes de tri

finpour {rduire de moiti la distance} distance distance div 2 fintantque

La complexit de ce tri est relativement difcile calculer. Sachez quelle dpend trs fortement de la squence de distances choisie. Trouver la suite des distances qui donne les meilleurs rsultats nest pas simple. Le choix trs courant dune suite de puissances de deux (celui de lalgorithme prsent) nest pas trs bon, car la complexit du tri est dans le pire des cas gale O(n2 ). Plusieurs squences, comme 1, 3, 7, . . . , 2k + 1, donnent des complexits dans le pire des cas gales O(n3/2 ). En 1969, D. K NUTH indique une complexit gale O(n1,25 ) pour la suite 1, 4, 13, 40, 121, 364, 1093, 3280, 9841, . . .. Cette suite a aussi lavantage dtre trs facile calculer, puisquil suft de multiplier le terme prcdent par 3 et de lui ajouter 1 : hk1 = 3hk + 1, ht = 1 et t = log3 n 1. Nous donnons la programmation en JAVA du tri avec cette dernire squence calcule et conserve dans le tableau tableDistances.
public void triShell(Comparateur<C> c) { // cration de la squence de distances de Knuth int [] tableDistances = new int[(int) Math.floor(Math.log(longueur())/Math.log(3))-1]; int h = 1; for (int i=0; i<tableDistances.length; i++) { tableDistances[i] = h; h = 3 * h + 1; } // tri Shell for (int k=tableDistances.length-1; k>=0; k--) { // trier par insertion les lments spars de distance int distance = tableDistances[k]; for (int i=distance+1; i<=longueur(); i++) { lment<V,C> x = ime(i); int j = i-distance; // rechercher la place de x dans la sous-liste ordonne // et dcaler simultanment while (j>0 && c.suprieur(ime(j).cl(),x.cl())) { affecter(j+distance,ime(j)); j-=distance; } // insrer x sa place affecter(j+distance,x); } } }

22.2

Tris internes

311

22.2.4

Tri par changes

Ces mthodes de tri procdent par changes de paires dlments qui ne sont pas dans le bon ordre, jusqu ce quil ny en ait plus. Nous prsentons deux tris : le plus mauvais et le meilleur des tris internes de ce chapitre. Tri bulles (Bubble sort) Comme pour le tri par slection, la ie itration, la sous-liste forme des lments de rang 1 i1 est trie. De plus, les cls des lments compris entre les rangs i et n sont suprieures celles de la sous-liste ordonne. En partant du rang n jusquau rang i, on change deux lments conscutifs chaque fois quils ne sont pas dans le bon ordre, de telle sorte que le plus petit trouve sa place au rang i. Le nom de tri bulles rete le fait que les lments les plus lgers (les plus petits) remontent la surface (i.e. vers le dbut de la liste). Au-dessous, chaque ligne correspond une tape et donne le rsultat des changes en partant de la n de la liste. Les nombreux changes effectus chacune des tapes ne sont pas indiqus faute de place. Par exemple, la premire tape 180 et 827 sont dabord changs, puis 11 est chang successivement avec toutes les valeurs de 631 53, soit au total neuf changes uniquement pour la premire tape.
|53 11 11 11 11 11 11 11 11 11 914 230 785 121 350 567 631 11 827 |53 914 230 785 121 350 567 631 180 53 |121 914 230 785 180 350 567 631 53 121 |180 914 230 785 350 567 631 53 121 180 |230 914 350 785 567 631 53 121 180 230 |350 914 567 785 631 53 121 180 230 350 |567 914 631 785 53 121 180 230 350 567 |631 914 785 53 121 180 230 350 567 631 |785 914 53 121 180 230 350 567 631 785 |827 180 827 827 827 827 827 827 827 827 914

Cet algorithme de tri scrit :


Algorithme TriBulles(l) {Rle: trie la liste l en ordre croissant des cls} pourtout i de 1 longueur(l)-1 faire {Invariant : la sous-liste de ime(l,1) ime(l,i-1) est trie} pourtout j de longueur(l) i+1 faire si cl(ime(l,j))<cl(ime(l,j-1)) alors {changer ime(l,j) avec ime(l,j-1)} ime(l,j) ime(l,j-1) finsi finpour finpour

Il est facile de voir que le nombre de comparaisons moyen, le meilleur et le pire, est identique celui du tri par slection directe, soit 1 (n2 n) = O(n2 ). 2

312

Chapitre 22 Algorithmes de tri

Dans le pire des cas, cest--dire quand la liste est trie en ordre inverse, les lments sont systmatiquement changs. Il y a n 1 changes la premire tape, n 2 la seconde, etc. Puisquil y a n 1 tapes, le nombre dchanges le pire est donc 1 (n2 n) = O(n2 ). En 2 revanche, il ny a aucun change si la liste est dj trie. On dmontre par dnombrement que le nombre dinversions dune liste l dlments distincts, cest--dire le nombre de couples (i, j) tels que i < j et i`me(l, i) > i`me(l, j) est e e 1 en moyenne gal 4 (n2 n). Il en rsulte que cest le nombre moyen dchanges de lalgorithme, et sa complexit est donc O(n2 ). Le tri rapide Invent par C.A.R. H OARE au dbut des annes soixante, le tri rapide (quicksort en anglais) doit son nom au fait quil est lun des meilleurs tris existants. On le retrouve dans les environnements de programmation de nombreux langages. Cest un tri par changes et partitions. Il consiste choisir une cl particulire dans la liste trier, appele pivot, qui divise la liste en deux sous-listes. Tous les lments de la premire sous-liste de cl suprieure au pivot sont transfrs dans la seconde. De mme, tous les lments de la seconde sous-liste de cl infrieure au pivot sont transfrs dans la premire. La liste est alors forme de deux partitions dont les lments de la premire possdent des cls infrieures ou gales au pivot, et ceux de la seconde possdent des cls suprieures ou gales au pivot. Le tri se poursuit selon le mme algorithme sur les deux partitions si celles-ci possdent une longueur suprieure un. chaque tape, pour crer les deux partitions de part et dautre du pivot, on parcourt simultanment la liste laide de deux indices, en partant de ses extrmits, et on change les lments qui ne sont pas dans la bonne partition. Le partitionnement sachve lorsque les indices se croisent. Par exemple, si nous choisissons comme pivot la valeur 350, le partitionnement provoque deux changes, mis en vidence ci-dessous :

53 914 230 785 121 350 567 631 11 827 180

T
i=1

T E '
j=11

lissue du partitionnement, la liste est organise de la faon suivante :


53 180 230 11 121 350 567 631 785 827 914

T
j=5

T
i=7

De plus, les afrmations suivantes sont vries :

22.2

Tris internes

313

k [1, i 1], cl(ime(l,k)) pivot k [j + 1, longueur(l)], cl(ime(l,k)) pivot k [j + 1, i 1], cl(ime(l,k)) = pivot Le partitionnement dune liste entre les rangs gauche et droit autour dun pivot est donn ci-dessous :
Algorithme partitionnement(l, gauche, droit) {Partitionnement dune liste l autour dun pivot} {entre les rangs gauche et droit} i gauche j droit pivot {choisir le pivot} rpter tantque cl(ime(l,i))<cl(pivot) faire i i+1 fintantque tantque cl(ime(l,j))>cl(pivot) faire j j-1 fintantque si i j alors changer(l,i,j) i i+1 j j-1 finsi {k[1,i-1], cl(ime(l,k)) pivot} {k[j+1,longueur(l)], cl(ime(l,k)) pivot} jusqu i>j {k[j+1,i-1], cl(ime(l,k))=pivot} {k[1,i-1], cl(ime(l,k))} pivot} {k[j+1,longueur(l)], cl(ime(l,k)) pivot}

Vous noterez que le balayage des sous-listes avec des tests dingalit stricte peut provoquer des permutations inutiles lorsque la liste contient des cls identiques. On pourrait les remplacer par des tests dingalit au sens large, mais dans ce cas le pivot ne jouerait plus son rle de sentinelle. En effet, imaginons que toutes les cls de la liste soient infrieures ou gales au pivot, le parcours ralis par la premire boucle fera sortir la variable i des bornes de la liste. Si on teste lgalit, il faut prvoir une autre gestion de sentinelle, ou plus simplement rcrire le parcours de la faon suivante :
tantque i j et cl(ime(l,i)) cl(pivot) faire i i+1 fintantque tantque i j et cl(ime(l,j)) cl(pivot) faire j j-1 fintantque

Le tri dune liste entre les rangs gauche et droit se poursuit par le partitionnement des deux partitions cres, selon la mme mthode. La mthode de tri rapide crite rcursivement en JAVA pour une sous-liste comprise entre les rangs gauche et droit est donne ci-dessous :
private void triRapide(Comparateur<C> c, int gauche, int droite) { int i=gauche, j=droit; C pivot = ime((i+j)/2).cl();

314

Chapitre 22 Algorithmes de tri

do { while (c.infrieur(ime(i).cl(), pivot)) i++; while (c.suprieur(ime(j).cl(), pivot)) j--; if (i<=j) { changer(i,j); i++; j--; } // invariant : // k[1,i-1], cl(ime(k)) pivot // k[j+1, longueur()], cl(ime(k)) pivot } while (i<=j); // k[j+1,i-1], cl(ime(k))=pivot // k[1,i-1], cl(ime(k)) pivot // k[j+1, longueur()], cl(ime(k)) pivot if (gauche<j) triRapide(c, gauche, j); if (droit>i) triRapide(c, i, droit); }

Pour trier une liste complte, il suft de donner les valeurs 1 et longueur(), respectivement, gauche et droit lors du premier appel de la mthode. Le tri rapide scrit simplement :
public void triHoare(Comparateur<C> c) { triRapide(c, 1, longueur()); }

La gure 22.4 montre les diffrentes tapes du tri sur la liste de rfrence. Les carrs indiquent les pivots choisis par la mthode prcdente, et les ovales entourent les partitions cres autour du pivot aprs changes.
53 180 230 11 121 350 567 631 785 827 914

53

180

121

11

230

567

631

785

827

914

53

11

121

180

567

631

827

914

11

53

121

53

121

11

53

121

180

230

350

567

631

785

827

914

F IG . 22.4 Tri rapide de la liste de rfrence.

Le choix du pivot conditionne fortement les performances du tri rapide. En effet, la complexit moyenne du nombre de comparaisons dans la phase de partitionnement dune liste de

22.2

Tris internes

315

longueur n est O(n), puisquon compare le pivot aux n 1 autres valeurs de la liste. Il y a n ou n + 1 comparaisons. Si le choix du pivot est tel quil divise systmatiquement la liste en deux partitions de mme taille, cest--dire que le pivot correspond la mdiane, le nombre de comparaisons sera gal n log2 n. En revanche, si chaque tape, le choix du pivot divise la liste en deux partitions de longueur 1 et n 1, les performances du tri chutent de faon catastrophique et le nombre de comparaisons est O(n2 ). Dans lexemple donn, le tri est optimal pour les partitionnements successifs de la partition de droite produite la premire tape ; le pivot est chaque fois la mdiane. Pour la partition de gauche, les choix successifs des pivots 230, 180 et 11 sont au contraire les plus mauvais. Dans le cas moyen, on a dmontr, sous lhypothse de cls diffrentes et quiprobables, que le nombre de comparaisons est 2n ln(n) 1, 38n log2 n ; sa complexit reste gale O(n log2 n). chaque tape de partitionnement, le nombre dchanges est dans le meilleur des cas gal 1, dans le pire des cas n/2 , et dans le cas moyen environ n/6. Pour un tri complet, il en rsulte que la complexit moyenne du nombre dchanges est O(n log2 n). Comment choisir le pivot ? Le choix du pivot doit rester simple, an de garder toute son efcacit la mthode. Dans la mthode programme plus haut, nous avons choisi llment du milieu. Nous aurions pu tout aussi bien choisir le pivot au hasard, ou le premier ou le dernier de la liste. Mais attention ces deux derniers choix, si la liste est dj trie ou inversement trie, lalgorithme donnera sa plus mauvaise performance. C.A.R. H OARE suggre de prendre la mdiane de trois lments, par exemple le premier, le dernier et celui du milieu. [BM93] ont galement propos des adaptations de lalgorithme an de garantir les performances du tri.

22.2.5

Comparaisons des mthodes

Nous donnons dans cette section quelques lments de comparaison des mthodes de tri internes prsentes dans ce chapitre, mis en lumire par des rsultats exprimentaux obtenus sur un Core 2 Duo T7400 2.18 GHz. Les mthodes de tri crites en JAVA ont t testes pour des listes dont les lments sont tirs au hasard, en ordre croissant et dcroissant. Pour les mthodes de tris simples, les longueurs des listes varient de 10 250 000 (au del les temps de calcul deviennent vraiment trop longs), et pour les mthodes labores, nous avons test les tris pour des listes de longueur maximale gale 1 200 000 lments. Pour des listes jusqu cinq mille lments environ, toutes les mthodes se valent. Le tri est effectu en un ou deux centimes de seconde. Au-del de cette valeur, il apparat des diffrences signicatives entre les mthodes simples (slection directe, tri bulles, insertion squentielle et dichotomique) et les mthodes labores (tri shell, tri en tas et tri rapide). Il faut environ trente-six minutes pour trier 250 000 lments avec une slection directe, alors que moins dune demi seconde suft au tri rapide. Parmi les mthodes simples, le tri bulles a les plus mauvaises performances, sauf dans le cas o la liste est dj trie, il est alors un peu meilleur que le tri par slection directe. Les tris par insertion sont meilleurs que les tris par slection, et dans le cas particulier dune liste dj trie, ils donnent les meilleurs rsultats de tous les tris puisque leur complexit est gale O(n). Dans le cas moyen, le tri par insertion dichotomique napporte pas damlioration spectaculaire cause du dcalage linaire. En JAVA, toutefois, lencombrement dun lment sera toujours celle dune rfrence, et les tris par insertion resteront meilleurs que

316

Chapitre 22 Algorithmes de tri

ceux par slection. Nous constatons que le tri par insertion dichotomique est le meilleur des tris lmentaires, mais reste nanmoins moins bon que les tris labors. La gure 22.5 montre des temps dexcution pour des listes quelconques. Les abscisses donnent la longueur de la liste, et les ordonnes le temps exprim en secondes.

F IG . 22.5 Mthodes simples.

Parmi les mthodes labores, le tri rapide est incontestablement le plus performant. Quelle que soit la faon de choisir le pivot (hasard, milieu ou mdiane), les temps de tri sont peu prs identiques. Notez que le tri rapide et le tri en tas possdent une complexit thorique en nombre de comparaisons assez semblable, et en fait meilleure pour le second. On constate, dans la pratique, que le premier tri est deux fois plus rapide que le second, certainement parce que le nombre de dplacements des lments est infrieur. Dautre part, certains auteurs ont remarqu qu partir dune certaine taille des partitions, le cot des appels rcursifs devient signicatif. partir dun certain seuil, environ 20 lments, on substitue au tri rapide une mthode de tri simple, par exemple un tri par insertion squentielle. Les rsultats que nous avons obtenus de faon exprimentale nont montr aucune amlioration, mais au contraire une lgre dgradation des performances. Le tri par distances dcroissantes donne de meilleurs rsultats que le tri en tas lorsque la liste est trie ou trie en ordre inverse. Mais nous avons aussi vri que le tri par distances dcroissantes reste nettement infrieur aux deux autres mthodes pour des listes quelconques. Les temps dexcution pour des listes quelconques par les mthodes labores sont donns par les courbes de la gure 22.6.

22.3

Tris externes

317

F IG . 22.6 Mthodes labores.

22.3

TRIS EXTERNES

On utilise des mthodes de tri externes lorsque les donnes trier ne peuvent pas toutes tre places en mmoire centrale. Cest une chose qui arrive assez frquemment dans les applications de gestion et de base de donnes. Les mthodes de tri externes dpendent des caractristiques de lenvironnement matriel et logiciel. On se place ici dans le cas de la gestion dlments placs sur chiers squentiels. La principale difcult des tris externes est la gestion des chiers auxiliaires, et en particulier den minimiser leur nombre. Les mthodes de tri externes sont gnralement bases sur la distribution de monotonies (sous-suites dlments ordonnes) et leur fusion par interclassement, comme par exemple, les tris quilibr, polyphas et par fusion naturelle. Dans cette section, nous prsentons ce dernier tri. Le tri par fusion naturelle Soit f le chier initial contenant les lments trier. Ce chier contient n monotonies naturelles. La mthode de tri ncessite deux chiers auxiliaires, g et h sur lesquels les n monotonies sont alternativement distribues. Dans le meilleur des cas, il y aura n/2 (ou n/2 et n/2 + 1) monotonies sur chacun des chiers auxiliaires aprs cette opration de distribution. Le tri consiste ensuite fusionner deux deux les monotonies de g et h sur f . Le chier f contient alors un nombre de monotonies infrieur ou gal n/2 + 1. Il est clair que la rptition de ce processus fait tendre le nombre de monotonies sur f vers 1 et le chier est alors tri. La gure 22.7 montre les trois tapes successives de distribution et de fusion ncessaires au tri de la suite de rfrence selon cette mthode.

318

Chapitre 22 Algorithmes de tri

53 914 230 785 121 350 567 631 53 914 121 350 567 631 180 11 827

11 827 180

B distribution r 230 785 j r fusion

53 230 785 914

11 121 350 567 631 827 180

B distribution r 11 121 350 567 631 827 j r fusion


11 53 121 230 350 567 631 785 827 914 180

53 230 785 914 180

B distribution 11 r j r180 fusion


11

53 121 230 350 567 631 785 827 914 53 121 180 230 350 567 631 785 827 914

F IG . 22.7 tapes du tri par fusion naturelle de la suite de rfrence.

Ce processus de tri fusion naturelle est donn par la mthode JAVA suivante :
public void fusionNaturelle(File f, Comparateur<C> c) throws Exception { File g = File.createTempFile("tmpG", "data"), h = File.createTempFile("tmpH", "data"); do { // rpartir alternativement les monotonies sur g et h distribuer(f,g,h,c); // fusionner g et h sur f } while (fusionner(g,h,f,c)!=1); // f de contient plus quune seule monotonie }

Notez que la classe File utilise ci-dessus est celle du paquetage java.io. Elle donne une reprsentation abstraite des noms de chier du systme de chiers sous-jacent. La mthode statique createTempFile cre un chier temporaire. Les chiers dlments doivent tre considrs comme des chiers de monotonies pourvues doprations spciques de manipulation de monotonies. Pour le tri, nous dnirons le type abstrait Fm avec les oprations particulires suivantes : fdf : Fm fdm : Fm copiermonotonie : Fm Fm copierlesmonotonies : Fm Fm fusionmonotonie : Fm Fm boolen boolen Fm Fm naturel Fm

Les oprations fdf(f) et fdm(f) indiquent respectivement si la n du chier f est atteinte ou si la n de sa monotonie courante est atteinte. Lopration copiermonotonie(f,g) copie la

22.3

Tris externes

319

monotonie courante de f sur g. Lopration copierlesmonotonies(f,g) copie sur g toutes les monotonies de f , partir de la monotonie courante, et renvoie le nombre de monotonies copies. Enn, lopration fusionmonotonie(f,g,h) crit sur h la monotonie rsultat de la fusion des monotonies courantes de f et g. La mise en uvre en JAVA du type abstrait Fm par extension des classes ObjectInputStream et ObjectOutputStream est assure par la classe gnrique FichierMonotoniesEntre. Elle est paramtre sur les types des valeurs et des cls des lments trier. Sa programmation ne pose pas de difcult, et elle est dailleurs laisse en exercice. Notez que seuls les chiers dentre doivent tre traits comme des chiers de monotonies. Lopration de distribution des monotonies sur les deux chiers auxiliaires est donne par la mthode distribuer :
private void distribuer(File f1, File f2, File f3, Comparateur<C> c) throws Exception // Rle : rpartit alternativement les monotonies du fichier f1 // sur les fichiers f2 et f3 { FichierMonotoniesEntre<V,C> f = new FichierMonotoniesEntre<V,C>(new FileInputStream(f1),c); ObjectOutputStream g = new ObjectOutputStream(new FileOutputStream(f2)), h = new ObjectOutputStream(new FileOutputStream(f3)); while (!f.fdf()) { f.copierMonotonie(g); if (!f.fdf()) f.copierMonotonie(h); } f.close(); g.close(); h.close(); }

Une fois les monotonies de f rparties sur g et h, il ne reste plus qu les fusionner sur f . Lalgorithme de fusion est classique, il parcourt simultanment les deux chiers g et h et fusionne les monotonies deux deux. La n de lun des deux chiers peut tre atteinte avant lautre. Dans ce cas, il est ncessaire de recopier toutes les monotonies restantes sur f . La mthode renvoie le nombre de monotonies crites sur f .
private int fusionner(File f1, File f2, File f3, Comparateur<C> c) throws Exception { FichierMonotoniesEntre<V,C> f = new FichierMonotoniesEntre<V,C>(new FileInputStream(f1), c), g = new FichierMonotoniesEntre<V,C>(new FileInputStream(f2), c); ObjectOutputStream h = new ObjectOutputStream(new FileOutputStream(f3)); int nbMono = 0; while (!f.fdf() && !g.fdf()) { f.fusionMonotonie(g,h); nbMono++; } // f.fdf() ou g.fdf()

320

Chapitre 22 Algorithmes de tri

if (!f.fdf()) // copier toutes les monotonies de f la fin de h nbMono+=f.copierLesMonotonies(h); else if (!g.fdf()) // copier toutes les monotonies de g la fin de h nbMono+=g.copierLesMonotonies(h); return nbMono; }

Dans les algorithmes de tri externe, seule la complexit associe aux dplacements des lments est vraiment intressante dans la mesure o le temps ncessaire un accs en mmoire secondaire est dun ordre de grandeur en gnral bien suprieur celui dune comparaison en mmoire centrale. chaque tape distribution-fusion, le nombre de monotonies est au pire divis par deux. Dans le pire des cas (chier n lments tri lenvers), il y aura log2 n tapes, chaque tape imposant n dplacements. Le nombre maximal de dplacements est donc n log2 n . Dans le cas moyen, si m est la longueur moyenne des monotonies, le nombre dtapes sera gal log2 (n/m) .

22.4

EXERCICES

Exercice 22.1. crivez la mthode triIdiot, qui trie une liste de longueur n dans un temps non born. La mthode du tri est la suivante : tirez deux indices diffrents au hasard compris entre 1 et n, changez les deux lments associs et regardez si la table est trie. Si la table est trie, on arrte ; sinon on recommence. Testez cette mthode ? Jusqu quelle longueur de liste cette mthode donne-t-elle un rsultat en un temps raisonnable ? Exercice 22.2. On dit quun tri est stable si, pour deux lments qui possdent la mme cl, lordre de leur position initiale est conserv dans la liste trie. Parmi les tris prsents dans ce chapitre, indiquez ceux qui sont stables. Exercice 22.3. Le tri par fusion procde par interclassement de sous-listes tries. Lalgorithme de ce tri sexprime bien rcursivement. On divise la liste trier en deux sous-listes de mme taille que lon trie rcursivement par fusion. Les deux sous-listes tries sont ensuite fusionnes par interclassement. Le tri par fusion dune liste entre les rangs gauche et droit est donn par :
Algorithme triFusion(l, gauche, droit) si gauche<droit alors milieu (gauche+droit)/2 triFusion(l, gauche, milieu) triFusion(l, milieu+1, droit) fusion(l, gauche, milieu, droit) finsi

22.4

Exercices

321

Quelle est la complexit de ce tri ? Quel en est son principal inconvnient ? Programmez en JAVA le tri par fusion. Exercice 22.4. Programmez le tri par distances dcroissantes avec la squence, propose par R. S EDGEWICK [Sed04], 1, 5, 19, 41, 109, 209, . . . dont les termes sont calculs de faon croissante partir des fonctions 94k 92k +1 et 4k 32k +1. Cette dernire squence donne en pratique de meilleurs rsultats que celle de D. K NUTH. Exercice 22.5. Programmez la mthode du tri rapide et faites des tests comparatifs en choisissant pour pivot : 1) le premier lment de la liste 2) la moyenne de la premire et de la dernire valeur de la liste 3) la valeur mdiane des trois premiers lments diffrents. Exercice 22.6. Modiez (lgrement) lalgorithme du tri rapide an de renvoyer la valeur mdiane dune liste. On rappelle que la valeur mdiane divise une liste en deux sous-listes de mme taille telles que tous les lments de la premire sont infrieurs ou gaux la mdiane, et tous les lments de la seconde lui sont suprieurs ou gaux. Exercice 22.7. Nous avons vu que la complexit thorique la pire dun tri par comparaison est O(n log2 n). Il est toutefois possible de trier en O(n) lorsquon possde des informations sur les lments. Prenons, par exemple, une liste de n entiers trier (ces n entiers sont distincts et compris entre 1 et n. Lalgorithme suivant trie la liste l dans un tableau t sans aucune comparaison et avec n transferts.
pourtout i de 1 n faire t[ime(l,i)] ime(l,i) finpour

Modiez cet algorithme de faon faire un tri sur place (sa complexit doit rester en O(n)). Exercice 22.8. La mthode de tri par paquets gnralise la mthode de lexercice prcdent. Pour une suite de n entiers pris sur lintervalle [1, m], on utilise un tableau t de longueur m initialis 0. Pour chaque entier i de la suite, on incrmente t[i] de un. la n, il suft de parcourir le tableau t du dbut pour obtenir la suite trie. crivez lalgorithme qui met en uvre cette mthode. Exercice 22.9. Nous voulons trier une suite de chanes de caractres dans lordre lexicographique (i.e. celui du dictionnaire). Pour cela, nous allons regrouper les chanes dans une table structure par paquets de longueur maximale m. Ces paquets sont constitus par des chanes de caractres dbutant par un mme groupe de k caractres, le k + 1e permettant de diffrencier chaque chane. Lorsquun paquet est plein, on cre une table dindirection permettant de discriminer sur cette k + 1e lettre. Par exemple, les chanes alexandre et anas sont dans un paquet (m = 2) et sont diffrencies par leur deuxime lettre (k = 2). La gure suivante montre leffet de lajout de la chane alice. Dnissez la structure de donnes qui reprsente la table qui mmorise les chanes de caractres. crivez lalgorithme dinsertion dune chane dans la table, puis lalgorithme qui afche sur la sortie standard toutes les chanes de caractres dans lordre lexicographique. Exercice 22.10. Programmez la classe FichierMonotoniesEntre du tri externe par fusion naturelle donn dans ce chapitre.

322

Chapitre 22 Algorithmes de tri

alexandre anas

alexandre alice

anas

Exercice 22.11. Le tri externe prsent interclasse les monotonies qui existent naturellement dans le chier trier. Une autre faon de procder est de lire les lments du chier par paquets de taille m, de trier en mmoire centrale chaque paquet laide dun tri interne, et de distribuer les monotonies ainsi construites sur les chiers auxiliaires. Le tri se poursuit par interclassement comme prcdemment. Si on dispose de 2k chiers, une moiti en lecture, et lautre en criture, une opration de fusion consistera interclasser au plus k monotonies dans un des chiers en criture. Quel est le nombre moyen dtapes ncessaires au tri de n lments si les monotonies sont de longueur m et sil y a k chiers en lecture ? Programmez cette mthode de tri externe pour k = 4. Notez que vous pouvez utiliser une le avec priorit pour trouver le minimum des k monotonies lors de linterclassement.

Chapitre 23

Algorithmes sur les graphes

Ce chapitre prsente quatre algorithmes sur les graphes parmi les plus classiques. Les algorithmes de recherche des composantes connexes et de fermeture transitive dun graphe sont utiles pour tester la connexit de ses sommets. Parmi les problmes de cheminement, lalgorithme de D IJKSTRA permet de trouver le chemin le plus court entre un sommet particulier et les autres sommets dun graphe valu. Nous verrons galement, les algorithmes A* et IDA* qui se servent dheuristiques pour trouver le cheminement le plus court dans le graphe. Enn, nous terminerons avec lalgorithme de tri topologique qui rsout des problmes dordonnancement des graphes orients sans cycle. La programmation en JAVA de ces algorithmes vient enrichir les classes qui implantent les interfaces gnriques Graphe et GrapheValu donnes au chapitre 18.

23.1

COMPOSANTES CONNEXES

Une manire de vrier sil existe un chemin entre deux sommets est de vrier sils appartiennent la mme composante connexe. On rappelle quun graphe non orient est compos dune ou plusieurs composantes connexes. Une composante connexe est forme de sommets tels quil existe toujours un chemin qui les relie. Une faon simple de trouver toutes les composantes dun graphe est dutiliser des parcours en profondeur ou en largeur. Nous avons vu la section 18.4 que de tels parcours partir dun sommet initial s accdaient aux sommets pour lesquels un chemin depuis s existait. On appelle arbre couvrant dun graphe connexe non orient G = (X, U ), le graphe partiel G = (X, U ) tel que U U et G est connexe et sans cycle. Pour trouver les composantes connexes dun graphe, on construit les arbres couvrants qui leur sont associs lors du parcours complet du graphe.

324

Chapitre 23 Algorithmes sur les graphes

s1

s9

s2 s4

s3 s10

s5 s6

s7

s8

F IG . 23.1 Graphe avec deux composantes connexes.

Le graphe de la gure 23.1 possde deux composantes connexes. Les deux arbres couvrants associs ce graphe sont donns par la gure 23.2.
s1 s9

s2 s4

s3

s10

s5 s6

s7

s8

F IG . 23.2 Deux arbres couvrants issus du graphe de la gure 23.1.

Lalgorithme suivant construit une liste darbres couvrants Ai , associs chaque composante connexe dun graphe non orient. Il fait un parcours en profondeur des sommets du graphe. Chaque sommet non marqu s est ajout dans larbre couvrant Ai . Chacun de ses successeurs non marqus x est ajout dans Ai et une arte (s, x) est cre. La construction de larbre couvrant se poursuit rcursivement avec les successeurs x.
Algorithme Composantes-Connexes(G, L) {Construit une liste L des arbres couvrants de chaque composante connexe du graphe non orient G} pourtout s de G faire si non marqu(s) alors {construire la ime composante connexe Ai dont la racine est le sommet s} ajouter le sommet s Ai cConnexes(G, s, Ai ) ajouter(L, Ai ) finsi finpour

23.2

Fermeture transitive

325

Algorithme cConnexes(G, s, A) {Parcourir en profondeur des successeurs du sommet s et construire la composante connexe A} mettre une marque sur le sommet s pourtout x de G tel que une arte(s,x) faire si non marqu(x) alors {crer une arte (s,x)} ajouter le sommet x A ajouter larte (s,x) cConnexes(G, x, A) finsi finpour

La complexit de la cration des arbres couvrants est la mme que celle du parcours complet en profondeur dun graphe, soit O(n2 ) si le graphe, n sommets et p artes, est reprsent par une matrice dadjacence, et O(max(n, p)) si le graphe est reprsent par des listes dadjacence. Nous allons voquer quelques problmes complmentaires lis la connexit des graphes, mais qui ne seront pas dvelopps. Pour une composante connexe, il est possible dassocier plusieurs arbres couvrants. Il est facile de voir que, si on supprime larte (s5, s6), dans larbre couvrant gauche de la gure 23.2 page 324 et que si on cre larte (s4, s6), on obtient un nouvel arbre couvrant pour la premire composante connexe. Lorsque les graphes sont valus, cest--dire si une valeur est associe chaque arte, un problme classique est celui de la recherche de larbre couvrant minimal, cest--dire larbre dont la somme des valeurs des artes est la plus petite. Lalgorithme de K RUSKAL et celui de P RIM apportent tous les deux une solution ce problme. Un graphe orient est fortement connexe, si pour tout sommet s et s , il existe la fois un chemin de s vers s et un chemin de s vers s. Pour ces graphes, il sagit donc de rechercher les composantes fortement connexes les plus grandes. Pour cela, les algorithmes mis en uvre drivent du parcours en profondeur.

23.2

FERMETURE TRANSITIVE

Le calcul de la fermeture transitive dun graphe sert aussi vrier lexistence, ou non, dun chemin entre deux sommets dun graphe. Mais, plutt que davoir faire une vrication pour deux points particuliers du graphe, il est bien souvent trs utile, dobtenir, au pralable, cette connaissance pour tous les sommets du graphe. La fermeture transitive dun graphe G = (X, U ) est gale au graphe G+ = (X, U + ) tel que pour tout arc (x, y) de G+ , il existe un chemin de longueur suprieure ou gale un dans G dextrmit initiale x et dextrmit nale y. La fermeture transitive est dite rexive, et note G , si la longueur du chemin peut tre gale 0. Dans ce cas, tout arc (x, x) est toujours prsent dans G . Remarquez que la notion de fermeture transitive nest pas propre aux graphes. En fait, elle peut tre applique toute relation binaire sur un ensemble E dlments dont il sagit de connatre laccessibilit par transitivit.

326

Chapitre 23 Algorithmes sur les graphes

Lalgorithme du calcul de la fermeture transitive dun graphe G, connu sous le nom dalgorithme de F LOYDWARSHALL, consiste ajouter, de faon incrmentale, pour tout sommet s de G, un arc (x, y) sil existe un chemin entre x et s et un chemin entre s et y. Lalgorithme procde de faon itrative en parcourant lensemble des sommets du graphe. Il sagit donc, chaque fois quune relation de transitivit entre deux sommets x et y peut tre tablie, denregistrer la relation entre x et y (voir la gure 23.3 page 326).

s z

x y
F IG . 23.3 Relation de transitivit entre les sommets x et y .

Lalgorithme F LOYDWARSHALL appliqu un graphe G = (X, U ) sexprime formellement comme suit :


Algorithme fermeture-transitive(G) pourtout s de X faire pourtout x de X faire pourtout y de X faire si s = x = y et arc(x, s) et arc(s,y) alors si arc(x,y) alors ajouter larc (x,y) finsi finsi finpour finpour finpour

La gure 23.4 montre les arcs qui ont t ajouts au graphe de la gure 18.1 page 211 par le calcul de cet algorithme, et la gure 23.5 montre la fermeture transitive de ce graphe. La complexit de cet algorithme est O(n3 ), pour un graphe G n sommets. Cet algorithme est assez coteux, et il est important que les oprations de test de lexistence et dajout dun arc dans le graphe soient en O(1). La reprsentation dun graphe par une matrice sera alors prfrable. Limplantation en JAVA La mthode gnrique donne ci-dessous calcule la fermeture dun graphe pass en paramtre. Les tests dexistence dun arc avec le sommet courant s permettent dviter certaines excutions de boucle, mais lalgorithme nen demeure pas moins born par n3 , et reste bien en O(n3 ).
public <S> void fermetureTransitive(Graphe<S> g) { for (S s : g) for (S x : g) if (x != s && g.arc(x,s))

23.3

Plus court chemin

327

s1

s9

s2 s4

s3

s5 s6

s7

s8

F IG . 23.4 Arcs issus de la fermeture transitive du graphe de la g. 18.1.

s1

s9

s2 s4

s3

s5 s6

s7

s8

F IG . 23.5 fermeture transitive du graphe de la gure 18.1.

for (S y : g) if (x != y && y != s && (g.arc(s,y))) if (! g.arc(x,y)) // larc nest pas dj prsent => lajouter g.ajouterArc(x,y); }

23.3
23.3.1

PLUS COURT CHEMIN


Algorithme de Dijkstra

Nous appellerons distance(x, y), la distance entre deux sommets x et y dun graphe G = (X, U ) orient valu, dnie comme la somme des valeurs associes chaque arc du chemin qui relie x et y. Lobjet des algorithmes de recherche de plus court chemin est la

328

Chapitre 23 Algorithmes sur les graphes

recherche de la distance minimale entre deux sommets. Lalgorithme de D IJKSTRA que nous allons prsenter maintenant, permet de calculer la distance minimale entre un sommet source et tous les autres sommets dun graphe valu orient. Cet algorithme ncessite des valeurs darc positives ou nulles. Lalgorithme de D IJKSTRA construit de faon itrative un ensemble solution S form de couples x = (sx , dx ), tels que pour tout sommet sx G, dx est gale la distance minimale entre un sommet source s et le sommet sx . Sil nexiste pas de chemin pour un sommet sx , par convention, sa distance avec s est gale linni, not . Ainsi, pour le graphe donn ci-dessous et le sommet source s1, lalgorithme renvoie lensemble S = {(s1, 0), (s2, 1), (s3, 4), (s4, 6), (s5, 2), (s6, )}.

s1 10 8

s2 7 1 s3 s5 2

s4 2

s6
Initialement, lensemble solution S est vide, et un ensemble E est form de couples (sx , dx ), tels que : dx = 0 si sx = s si sx = s

Pour ce graphe, les valeurs initiales des ensembles S et E sont telles que : S= E = {(s1, 0), (s2, ), (s3, ), (s4, ), (s5, ), (s6, )} Lensemble S est construit progressivement de faon itrative. chaque itration, il existe un lment m = (sm , dm ) E de distance minimale, telle que x E, dm = min(dx ). Cet lment est retir de lensemble E et ajout dans S. Afrmation : la distance dm est la distance du plus court chemin entre s et sm . Ensuite, les distances dx de chaque x E qui possde un arc avec sm sont recalcules de telle faon que :
si dm + valeurArc(sm ,sx ) < dx alors dx dm + valeurArc(sm ,sx ) finsi

o valeurArc(sm ,sx ) est la valeur de larc entre le sommet sm et le sommet sx . la dernire itration, lensemble E est vide, et S contient la solution.

23.3

Plus court chemin

329

Lalgorithme appliqu au graphe de la page prcdente produit les six itrations suivantes :
S {(s1, 0)} {(s1, 0), (s2, 1)} {(s1, 0), (s2, 1), (s5, 2)} {(s1, 0), (s2, 1), (s5, 2), (s3, 4)} {(s1, 0), (s2, 1), (s5, 2), (s3, 4), (s4, 6)} {(s1, 0), (s2, 1), (s5, 2), (s3, 4), (s4, 6), (s6, )} E {(s2, 1), (s3, ), (s4, 10), (s5, ), (s6, )} {(s3, 8), (s4, 10), (s5, 2), (s6, )} {(s3, 4), (s4, 6), (s6, )} {(s4, 6), (s6, )} {(s6, )} {}

Lalgorithme du plus court chemin de D IJKSTRA scrit formellement comme suit :


Algorithme Dijkstra(G, s) {initialisations} S E {(sx , dx ) / sx G, dx = 0 si sx = s et dx = si sx = s} {construire lensemble S des plus courts chemins} tantque E = faire {Invariant : soit m E, k E, dm = min(dk )} { k S, dk = distance minimale entre s et sk } E E - {m} S S {m} {recalculer les distances dans E} pourtout x de E tel que un arc(sm ,sx ) faire si dm + valeurArc(sm ,sx ) < dx alors dx dm + valeurArc(sm ,sx ) finsi finpour fintantque {S = {(sx ,dx ), sx G, dx = distance minimale entre s et sx } } rendre S

Montrons que cet algorithme calcule bien lensemble solution S des plus courts chemins, tel que x S, dx est le plus court chemin de s au sommet sx . Commenons par le choix de m. Il faut montrer que dm est bien la distance du plus court chemin de s sm . Si ce ntait pas le cas, il existerait un n E (voir la gure 23.6), tel que dn + distance(sn , sm ) dm . Or, on a dm dn et, par hypothse, les valeurs des arcs ne peuvent tre ngatives. Donc n ne peut exister, et m possde le sommet sm de distance minimale par rapport s, et il est ajout dans S. On en dduit par rcurrence, que tous les prdcesseurs de sm , dans le plus court chemin de s sm , appartiennent exclusivement S. De mme, pour tout x = (sx , dx ) E, avec dx = , dx est la distance dun meilleur chemin entre s et sx , litration courante, et dont les sommets prdcesseurs de sx sont dans S. Il est vident, que la modication des valeurs des distances dans E, aprs lajout de m dans S, maintient cette dernire proprit, et permet, de trouver un meilleur chemin, sil existe, passant obligatoirement par sm .

330

Chapitre 23 Algorithmes sur les graphes

S E
s m

F IG . 23.6 La distance entre s et m est minimale.

Limplantation en Java Dans cette section, nous prsentons la programmation en JAVA de lalgorithme D IJKSTRA. La mthode gnrique Dijkstra prend en paramtre un GrapheValu avec des valeurs darcs de type Integer. Les couples des ensembles E et S sont reprsents par le type lment donn la page 245. La valeur de llment sera le sommet, et la cl sa distance au sommet source. Pour accrotre la lisibilit de la mthode Dijkstra, nous dnissons les trois mthodes prives suivantes :
// Cette mthode renvoie le sommet de llment e private <S> S sommet(lment<S,Integer> e); // Cette mthode renvoie la distance de llment e private <S> int dist(lment<S,Integer> e); // Cette mthode change la distance de llment e private <S> void changerDist(lment<S,Integer> x, Integer d);

Lcriture de ces mthodes ne pose aucune difcult et est laisse en exercice. Notez que pour la dernire mthode, il faut ajouter la classe lment une mthode changerCl, pour modier la valeur de la cl dun lment. Les ensembles E et S sont des objets de type Ensemble, une interface gnrique qui dnit les oprations dun type abstrait Ensemble, dont limplantation sera discute la section suivante. Pour le calcul des plus courts chemins, la classe Ensemble devra au moins fournir les mthodes suivantes :
public interface Ensemble<T> extends Collection<T> { // Cette mthode ajoute llment e lensemble courant public void ajouter(T e); // Cette mthode renvoie true si lensemble courant est vide, // et false sinon public boolean vide(); // Cette mthode supprime le plus petit lment de lensemble courant // et renvoie sa valeur public T supprimerMin(); // Cette mthode renvoie une numration des lments de lensemble public Iterator<T> iterator(); }

23.3

Plus court chemin

331

Les classes dimplantation de linterface Ensemble fournissent des constructeurs qui permettent linitialisation dun comparateur des lments de lensemble. Dans notre mthode Dijkstra les ensembles construits sont munis dun comparateur des cls entires des lment. Nous pouvons donner lcriture complte de la mthode qui renvoie lensemble des plus courts chemins dun sommet s tous les autres sommets du graphe. Notez que pour viter la confusion entre le type gnrique S des sommets du graphe et le nom S de lensemble solution, nous noterons ce dernier sol dans la mthode ci-dessous.
// calcul des plus courts chemins entre le sommet s // et les autres sommets du graphe, selon lalgorithme de D I J K S T R A // et renvoie lensemble solution public <S> Ensemble<lment<S,Integer>> Dijkstra(GrapheValu<S, Integer> g, S s) { Ensemble<lment<S,Integer>> E = new EnsembleListe<lment<S,Integer>>( new ComparateurDeClEntire()), sol = new EnsembleListe<lment<S,Integer>>( new ComparateurDeClEntire()); // initialiser lensemble E E.ajouter(new lment<S,Integer>(s, 0)); for (S x : g) if (x != s) E.ajouter(new lment<S,Integer>(x, Integer.MAX_VALUE)); // construire lensemble sol des plus courts chemins while (!E.vide()) { // soit m=(sm ,dm ) E, k E, sm = min(dk ) // k sol, dk = distance minimale entre s et sk lment m = E.supprimerMin(); sol.ajouter(m); // pour tout sommet x de E qui possde un arc avec m for (lment<S,Integer> x : E) if (g.arc(sommet(m),sommet(x))) { int d=dist(m) + g.valeurArc(sommet(m),sommet(x)); if (d<dist(x)) changerDist(x,d); } } } // sol = {(sx ,dx ), sx g, dx = distance minimale entre s et x} return sol; }

Complexit de lalgorithme La complexit de lalgorithme de D IJKSTRA dpend de la reprsentation de lensemble E. Nous allons nous intresser au nombre de comparaisons effectues dans le pire des cas par lalgorithme pour un graphe de n sommets et p arcs.

332

Chapitre 23 Algorithmes sur les graphes

Le corps de la boucle principale est effectu n fois. Une premire srie de comparaisons est faite lors de la recherche de llment m dans lensemble E. Si cet ensemble est reprsent par une liste linaire non ordonne, le nombre de comparaisons pour rechercher et supprimer m, est gal au cardinal de E. Il y aura donc en tout n(n+1)/2 comparaisons, la complexit est alors O(n2 ). Cette complexit peut tre amliore : si lensemble E est reprsent par un tas, laccs m est en O(1), et sa suppression demande au pire log2 (n), soit au total n log2 (n) comparaisons. Dans la seconde boucle, le test de vrication dadjacence des sommets sm et sx est excut n(n 1)/2 fois. La complexit est O(n2 ). Mais, on peut remarquer que le test dajustement des distances nest faire que d+ (sm ) fois, le demi-degr extrieur du sommet de sm . Si le nombre darcs p est petit devant son maximum, n2 , et si laccs au ie successeur du sommet sm peut tre excut en O(1), il sera plus judicieux de rcrire la seconde boucle de la faon suivante :
pourtout i de 1 degrExt(sm ) faire sx imeSucc(sm , i) dans E si dm + valeurArc(sm ,sx ) < dx alors dx dm + valeurArc(sm ,sx ) finsi finpour

Toutefois, cette criture ncessite un moyen de retrouver le dx correspondant au sx pour la mise jour des distances dans E. Si cette correspondance peut tre obtenue en O(1), la complexit de la modication des distances est O(p). Au total, la complexit de lalgorithme de D IJKSTRA est O(n2 ) pour des graphes denses avec E reprsent par une liste, alors quelle est O(n log2 n + max(n2 , p)) avec E reprsent par un tas.

23.3.2

Algorithme A*

La complexit de lalgorithme de D IJKSTRA, tant spatiale que temporelle, le rend en pratique inutilisable lorsque les graphes parcourir sont de trs grande taille, comme a peut tre le cas pour la rsolution automatique de jeux de type taquin1 . Pour rsoudre de tels problmes, une amlioration de lalgorithme de D IJKSTRA, appele A*, a t propose par H ART, N ILSSON et R APHAEL en 1968 [HNR68]. Lide principale de lalgorithme A* est de passer de sommet en sommet en privilgiant chaque fois les sommets qui offrent un cot f (p) le meilleur, cest--dire en se dirigeant vers le sommet nal en passant par les sommets qui semblent donner le chemin le plus direct. Pour chaque sommet p dun graphe valu G, on calcule le cot f = g + h, o g(p) est la longueur du chemin le plus court connu entre le sommet de dpart s et p, et h(p) lestimation de la longueur minimale du chemin entre p et le sommet nal t. Lalgorithme A*
1 Le jeu de taquin, invent la n du XIXe sicle aux tats-Unis, est un damier de 16 cases sur lequel coulissent 15 carreaux numrots quil sagit de rordonner. Le nombre de combinaisons possibles est gal 16! = 20 922 789 888 000. On modlise cet ensemble de congurations par un graphe o chaque sommet est une conguration particulire du taquin, et o les arcs qui relient un sommet particulier ses successeurs (entre 2 et 4) correspondent aux congurations possibles du taquin aprs le dplacement dun carreau. Le jeu de taquin consiste rechercher un chemin, le plus court si possible, entre deux sommets, en gnral entre une conguration quelconque et la conguration initiale du jeu de taquin.

23.3

Plus court chemin

333

appartient la famille des algorithmes de recherche heuristiquement ordonne (RHO). Il se sert dune fonction dvaluation h, appele heuristique, qui dnit une estimation infrieure de la distance entre le sommet courant p et le sommet darrive t. On dit que h est minorante si p G, h(p) h (p), o h (p) est la longueur du chemin le plus court entre p et t. Dans le cas de la rsolution dun taquin, une heuristique habituelle est la distance de M ANHATTAN2 . Si h est minorante, alors A* est dit admissible, cest--dire quil donne chaque fois un chemin minimal (ou optimal) de s t, sil existe. Lalgorithme A* scrit formellement comme suit :
Algorithme A (G, s, t) variables ouvert, ferm : Ensemble trouv faux f(s) h(s) ouvert {s} rpter {retirer le meilleur sommet de lensemble ouvert et lajouter dans ferm} courant leMeilleur(ouvert) ouvert ouvert - {courant} ferm ferm {courant} si courant = t alors trouv vrai sinon {poursuivre la recherche du chemin} pourtout succ de G tel qu un arc(courant,succ) faire {on traite un successeur du sommet courant} f(succ) g(s, succ) + h(succ) parent(succ) courant si succ ferm alors ouvert ouvert {succ} sinon {succ dj prsent dans ferm} soit aux ce sommet dans ferm si f(succ) < f(aux) alors ferm ferm - {aux} ouvert ouvert {succ} finsi finsi finpour finsi jusqu vide(ouvert) ou trouv si trouv alors {reconstituer le chemin solution partir de lensemble ferm en parcourant les parents depuis le sommet t darrive} sinon {lensemble ouvert est vide pas de solution} finsi

2 Pour

deux points x et y de Rn , leur distance de M ANHATTAN est gale

Pn

i=1

|xi yi |.

334

Chapitre 23 Algorithmes sur les graphes

A* gre deux ensembles, qui contiennent des sommets du graphe. Le premier, appel ouvert, contient les sommets examins lors du parcours. Le second, appel ferm, contiendra les sommets retenus (ceux dont le cot f est le plus bas). Cest partir de lensemble ferm qu la n de lalgorithme le chemin solution entre le point dorigine s et le point darrive t sera obtenu, en parcourant les parents depuis le sommet nal t. Comme pour lalgorithme de D IJKSTRA, le choix dune le avec priorit mise en uvre laide de tas pour reprsenter lensemble ouvert sera plus efcace quune liste linaire. La complexit temporelle de A* dpend du choix de lheuristique. Le nombre de sommets traits peut tre, dans le pire des cas, exponentiel par rapport la longueur du chemin solution. Il est clair que plus lheuristique choisie est proche de h , lheuristique optimale, plus A* sera efcace. Linconvnient majeur de A* est sa gourmandise en espace mmoire. L encore, dans le pire des cas, le nombre de sommets mmoriss devient exponentiel, et la recherche dun plus court chemin avec A* est alors impraticable mme pour les mmoires de trs grandes capacits actuelles. IDA* est une variante de A* qui permet de rduire la complexit spatiale.

23.3.3

Algorithme IDA*

En 1985, R. E. KORF propose lalgorithme IDA* (Iterative-Deepening A*) [Kor85] qui, contrairement A* qui traite dabord le sommet de plus petite valuation, dveloppe ltat le plus profond dabord pourvu que son cot ne dpasse pas un certain seuil. Le trs gros avantage de IDA* sur A*, cest qu tout moment, seuls sont mmoriss les sommets joignant s au sommet courant p, lexclusion de tous les autres. Le nombre de sommets effectivement mmoriss est ainsi considrablement rduit, permettant le parcours de graphe de grande taille. IDA* procde de faon itrative par augmentation progressive du seuil. Au dpart, le seuil est gal h(s). chaque itration, IDA* effectue une recherche en profondeur, laguant chaque branche dont le cot f = g + h est suprieur au seuil. Si la solution nest pas trouve, chaque nouvelle itration la valeur du seuil est modie, elle devient gale au minimum des valeurs qui ont dpass le seuil prcdent. Notez qu chaque nouvelle itration le parcours en profondeur refait tout ou en partie le travail dexploration du parcours de litration prcdente. Cela peut sembler coteux mais R. E. KORF montre quen fait a ne lest pas car le plus gros du travail se fait au niveau le plus profond de la recherche. Dautre part, sil nexiste pas de chemin pour atteindre le sommet darrive, il faut prvoir de dnir lavance un seuil maximal partir duquel lalgorithme considrera quil ny a pas de solution. R. E. KORF a montr que sous les mmes conditions que A*, IDA* est complet et admissible. Lalgorithme IDA* est donn ci-dessous :
Algorithme IDA (G, s, t) variables chemin : liste rsolu : boolen seuil, seuil_max : rel
seuil h(s) rsolu faux seuil_max ...

23.4

Tri topologique

335

rpter chemin {s} seuil rechercheEnProfondeur(G, s, seuil) jusqu rsolu ou seuil > seuil_max si rsolu alors {la file chemin est la solution} sinon {pas de solution} finsi

chaque itration, le parcours en profondeur est assur par la fonction rcursive rechercheEnProfondeur suivante :
fonction rechercheEnProfondeur(G, courant, seuil) : rel si h(courant) = 0 alors rsolu vrai rendre 0 finsi nouveau_seuil + pourtout succ de G tel qu un arc(courant,succ) faire {ajouter succ dans la file} chemin chemin + {succ} si g(courant,succ) + h(succ) seuil alors {poursuivre la recherche en profondeur} b g(courant, succ) + rechercheEnProfondeur(succ, seuil - g(courant,succ)) sinon {laguer} b g(courant, succ) + h(succ) finsi si rsolu alors rendre b sinon {retirer succ de la file} chemin chemin - {succ} {calculer le nouveau seuil pour la prochaine itration} nouveau_seuil min(nouveau_seuil, b) finsi finpour {tous les successeurs de courant ont t traits et la solution na pas encore t trouve renvoyer le seuil pour la prochaine itration} rendre nouveau_seuil finfonc

23.4

TRI TOPOLOGIQUE

Le tri topologique dnit un ordre (partiel) sur les sommets dun graphe orient sans cycle. Une relation apparat-avant compare deux sommets du graphe. Le tri renvoie comme rsultat

336

Chapitre 23 Algorithmes sur les graphes

une liste linaire de sommets ordonns de telle faon quaucun sommet napparat avant un de ses prdcesseurs. Cette relation impose de fait que le graphe soit sans cycle. La relation dordre du tri topologique est partielle, et le rsultat du tri peut alors ne pas tre unique, dans la mesure o deux sommets peuvent ne pas tre comparables. Prenons lexemple trivial de la gure 23.7. Le tri de ces trois sommets rend les suites < s1 s2 s3 > ou < s2 s1 s3 >.
s1 s3 s2
F IG . 23.7 Les sommets s1 et s2 sont incomparables.

La liste de sommets L est construite de faon itrative par lalgorithme du tri topologique que nous allons dcrire maintenant. Initialement, on associe, dans une table nbpred, chaque sommet de G son nombre de prdcesseurs, cest--dire son demi-degr intrieur. Tous les sommets sans prdcesseur sont mis dans un ensemble E. Puisque le graphe est sans cycle, il existe au moins un sommet sans prdcesseur, et E = . Un sommet s de E est choisi, supprim de E et ajout L. Le nombre de prdcesseurs de tous les sommets successeurs de s, qui appartiennent G mais pas L, est alors dcrment de 1. Si aprs dcrmentation, le nombre de prdcesseurs dun sommet x est gal 0, alors x est ajout E. Lorsque E est vide, tous les sommets de G sont dans L, et le tri est achev. Lalgorithme repose sur lafrmation que les prdcesseurs de tous les sommets de E sont dans L. Notez que le choix du sommet s dans E est quelconque, puisque les sommets de E ne sont pas comparables. De faon formelle, lalgorithme du tri topologique scrit :
Algorithme Tri-Topologique(G, L) {Antcdent : G graphe orient sans cycle} {Consquent : L liste des sommets de G ordonns}
{construire lensemble E et la table des prdcesseurs} E pourtout x de G faire nbpred[x] d (x) si nbpred[x]=0 alors E E {x} finsi finpour {E contient au moins un sommet} tantque E = faire {Invariant : x E, nbpred[x]=0 et arc(y,x) y L} soit s E ajouter(L,s) E E-{s} pourtout x de G adjacent s faire {Invariant : x L} nbpred[x] nbpred[x]-1

23.4

Tri topologique

337

si nbpred[x]=0 alors E E {x} finsi finpour fintantque {L contient tous les sommets de G ordonns} rendre L

Cet algorithme appliqu au graphe donn par la gure 23.8 de la page 337 renvoie la liste < s1 s4 s3 s2 >. Le tableau suivant montre les valeurs successives prises par E et L, ainsi que le nombre de prdcesseurs des sommets de G qui ne sont pas encore dans L.
s1 s2 s4
F IG . 23.8 Tri topologique.

s3

E s1, s4 s4 s3 s2

L s1 s1, s4 s1, s4, s3 s1, s4, s3, s2

nbpred nbpred[s2] = 3, nbpred[s3]=2 nbpred[s2] = 2, nbpred[s3]=1 nbpred[s2] = 1

23.4.1

Limplantation en Java

Nous programmons le tri topologioque laide de la mthode gnrique triTopologique donne ci-dessous. Lensemble E et la liste L sont simplement reprsents par deux les dont les lments sont des sommets. La table des prdcesseurs est une table dadressage dispers dont les valeurs sont de type Integer et les cls de sommets. Pour tout sommet, la mthode sommetsAdjacents renvoie lnumration de ses successeurs.
public <S> File<S> triTopologique(Graphe<S> g) { Table<Integer,S> nbPred = null; File<S> E = new FileChane<S>(); File<S> L = new FileChane<S>(); nbPred = new HashCodeFerm<Integer,S>(new ClSommet()); // construire lensemble E et la table des prdcesseurs for (S s : g) { int np = g.demiDegrInt(s); if (np == 0) E.enfiler(s); nbPred.ajouter(new lment<Integer,S>(np, s)); }

338

Chapitre 23 Algorithmes sur les graphes

// E contient au moins un sommet while (!E.estVide()) { // Invariant : x E, nbPred[x]=0 et arc(y,x)y L S s = E.premier(); E.dfiler(); L.enfiler(s); Iterator<S> e = g.sommetsAdjacents(s); while (e.hasNext()) { S x = e.next(); // Invariant : x L lment<Integer, S> ex = nbPred.rechercher(x); int np = ex.valeur().intValue()-1; ex.changerValeur(np); if (np == 0) E.enfiler(x); } } // L contient tous les sommets de g ordonns return L; } // fin triTopologique

Pour un graphe n sommets et p arcs, la complexit du tri topologique est, au pire, O(n + p) si le graphe est reprsent par des listes dadjacence et O(n2 ) sil utilise une matrice dadjacence. Si le graphe est reprsent par des listes dadjacence, la cration de la table des prdcesseurs demande un parcours des n sommets du graphe, et le calcul du demi-degr de chaque sommet rclame p tests au pire. La complexit est O(n p). Notez que cette complexit peut tre ramene O(n + p), si la table est cre lors dun parcours en profondeur ou en largeur du graphe. Cette amlioration est laisse en exercice. Si le graphe est reprsent par une matrice, la complexit de la cration de la table est toujours au pire O(n2 ). Puisquil na pas de cycle, chaque sommet s du graphe est trait une seule fois dans la phase de tri proprement dite. Le parcours de ses successeurs est alors proportionnel p. Il en rsulte une complexit gale O(n + p).

23.4.2

Existence de cycle dans un graphe

Le tri topologique sapplique un graphe sans cycle. Mais que se passe-t-il si lalgorithme est appliqu un graphe qui possde un ou plusieurs cycles ? Une telle situation conduit au fait quil nexiste pas de sommet s tel que nbpred[s]=0. Il en rsulte que E est vide et lalgorithme sarrte. La liste renvoye par le tri exclut les sommets qui forment le cycle. Le tri topologique est alors un moyen de vrier labsence ou la prsence de cycle dans un graphe.

23.4.3

Tri topologique inverse

Une autre mthode pour effectuer un tri topologique est de raliser un parcours en profondeur postxe du graphe (voir lalgorithme page 220). Chaque sommet s est ajout dans la liste L aprs le parcours de ses successeurs. Cette mthode est appele tri topologique inverse car la liste obtenue est en ordre inverse, puisque les sommets apparaissent aprs leurs successeurs.

23.5

Exercices

339

Pour le graphe de la gure 23.8, cet algorithme construit la liste < s2 s3 s4 s1 >, et propose un second tri topologique valide du graphe, < s1 s4 s3 s2 >. La complexit de cette mthode est celle du parcours en profondeur, cest--dire O(n2 ) si le graphe est reprsent par une matrice dadjacence et O(max(n, p)) sil est reprsent par des listes dadjacence.

23.4.4

Limplantation en Java

La programmation de la mthode triTopologiqueInverse consiste simplement appeler la mthode parcoursProfondeurPostfixe avec une opration qui construit la liste des sommets. Cette opration implante linterface Opration, donne la page 222, laquelle nous avons ajout la mthode rsultat qui retourne un rsultat nal de parcours.
public class OprationTriTopo<T> implements Opration<T> { private File<T> f; public OprationTriTopo() { f = new FileChane<T>(); } public Object excuter(T e) { f.enfiler(e); return null; } public Object rsultat() { return f; } }

La mthode triTopologiqueInverse scrit simplement :


public <S> File<S> triTopologiqueInverse(Graphe<S> g) { Opration<S> op = new OprationTriTopo(); g.parcoursProfondeurPostfixe(op); return (File<S>) op.rsultat(); }

23.5

EXERCICES

Exercice 23.1. Trouvez larbre couvrant du graphe donn par la gure suivante :
s1 s2 s3

s4

s5

s6

s7

s8

s9

340

Chapitre 23 Algorithmes sur les graphes

Exercice 23.2. Montrez quil existe un seul arbre couvrant minimal pour un graphe valu connexe (non orient) dont les valeurs des artes sont positives et distinctes. Exercice 23.3. Montrez sur un exemple que lalgorithme de D IJKSTRA donne un rsultat faux si un arc possde une valeur ngative. Exercice 23.4. Programmez lalgorithme de D IJKSTRA en utilisant un tas et une boucle qui ne parcourt que les successeurs de m dans E. Exercice 23.5. Pour rechercher le plus court chemin entre tout couple de sommets dun graphe, il est bien sr possible dappliquer itrativement lalgorithme de D IJKSTRA en prenant chacun des sommets du graphe comme source. Lalgorithme de F LOYD donne une solution trs simple ce problme. La mthode consiste considrer chacun des n sommets dun graphe, appelons-le s, comme intervenant possible dans la chane qui lie tout couple de sommets (x, y). Si la distance d(x, s) + d(s, y) est infrieure d(x, y), alors on a trouv une distance minimale entre x et y qui passe par le sommet s. Lalgorithme de F LOYD utilise une matrice carre n n pour mmoriser les distances calcules au fur et mesure. Il sexprime comme suit :
Algorithme Floyd(G,d) {initialiser la table des distances d} x,y G, d[numro(x),numro(y)] valeurArc(x,y) {calculer tous les plus courts chemins} pourtout s de 1 n faire pourtout x de 1 n faire pourtout y de 1 n faire si d[x,s]+d[s,y] < d[x,y] alors d[x,y] d[x,s]+d[s,y] finsi finpour finpour finpour rendre d

Appliquez cet algorithme sur le graphe de la page 328. Montrez que cet algorithme est valide. Quelle est sa complexit ? Est-il plus efcace que celui qui consiste appliquer n fois lalgorithme de D IJKSTRA ? Est-ce que lalgorithme de F LOYD prcdent peut sappliquer des valeurs darcs ngatives ? Programmez cet algorithme en JAVA. Exercice 23.6. En utilisant une matrice d de boolens (i.e. une matrice dadjacence), modiez lalgorithme de F LOYD an de calculer la fermeture transitive rexive du graphe. Cet algorithme est connu sous le nom dalgorithme de WARSHALL. Exercice 23.7. Programmez en JAVA, les algorithmes A* et IDA* pour rsoudre des taquins 16 cases. Comparez lefcacit des algorithmes. Utilisez et comparez les heuristiques suivantes : 1. le nombre de cases mal places ; 2. la distance de M ANHATTAN. Exercice 23.8. Modiez lalgorithme du tri topologique an de retourner la valeur boolenne vrai si le graphe possde un cycle, et la valeur faux dans le cas contraire.

23.5

Exercices

341

Exercice 23.9. Est-il possible de passer par tous les sommets dun graphe sans emprunter deux fois la mme arte ? Essayez de trouver ce chemin sur les deux graphes donns par la gure 23.9 page 341.

s1

s1

s2 s2
s3

s4 s6

s4 (a)

s3 (b)
F IG . 23.9 .

s5

Ce problme est connu sous le nom de chemin eulrien. E ULER3 a dmontr la condition ncessaire lexistence dun tel chemin dans un graphe connexe : soit il nexiste aucun sommet de degr impair, soit il existe deux sommets de degr impair. Si tous les sommets du graphe possdent un degr pair, il sagit alors dun cycle eulrien (i.e. les deux extrmits du chemin sont identiques). Sil existe deux sommets de degr impair, ce sont les extrmits du chemin. Appliquez cette condition aux deux graphes prcdents. Programmez une mthode qui vrie cette condition. Pour trouver le chemin eulrien E dun graphe G = (X, U ), on part dun sommet source (on prendra nimporte quel sommet si tous les sommets ont un degr pair, ou de lun des deux sommets de degr impair), et lon procde selon un parcours en profondeur de la forme :
Algorithme parcoursEuler(s, U, E) {Antcdent : s sommet origine du parcours U ensemble des artes du graphe parcourir Consquent : E ensemble des artes parcourues et U=U-E}
si v X tel que arte(s,v) U alors parcoursEuler(v, U-[s,v], E [s,v]) finsi

Au dpart, lensemble E des artes qui forment le chemin eulrien est initialis . Lorsque cet algorithme sachve, deux cas de gure se prsentent : soit U = et le chemin eulrien de G a t trouv, soit U = et seule une partie du graphe a t parcourue.
3 L. E ULER , mathmaticien suisse (1707-1783). Il a souvent t dit que la rsolution de ce problme marque lorigine de la thorie des graphes.

342

Chapitre 23 Algorithmes sur les graphes

Dans ce dernier cas, il faut recommencer lalgorithme partir dun des sommets du chemin E qui possde une arte dans U non parcourue. Montrez que sil reste des artes non parcourues, le graphe partiel comporte toujours un chemin eulrien. crivez en JAVA une mthode qui renvoie, sil existe, le chemin eulrien dun graphe.

Chapitre 24

Algorithmes de rtro-parcours

Les problmes qui mettent en jeu des algorithmes de rtro-parcours sont des problmes pour lesquels lalgorithme solution ne suit pas une rgle xe. La rsolution de ces problmes se fait par tapes et essais successifs. chaque tape, plusieurs possibilits sont offertes. On en choisit une et lon passe ltape suivante. Ces choix successifs peuvent conduire une solution ou une impasse. Dans ce dernier cas, il faudra revenir sur ses pas et essayer de nouvelles possibilits, ventuellement jusqu leur puisement. Cest une dmarche que lon adopte, par exemple, dans le parcours dun labyrinthe. Toutes les tapes, ainsi que les choix que lon peut faire chacune de ces tapes, modlisent un arbre de dcision. Chaque nud de cet arbre est une des tapes o sont proposs les choix. La recherche dune solution consiste donc parcourir cet arbre. Les branches qui stendent de la racine aux feuilles terminales de larbre sont les solutions potentielles. En gnral, les mthodes de rtro-parcours ne construisent pas explicitement cet arbre de dcision, il est purement virtuel. Dans ce chapitre, nous prsenterons les critures rcursives et itratives des algorithmes de rtro-parcours donnant, si elles existent, une solution particulire ou toutes les solutions possibles. Nous utiliserons ces algorithmes pour rsoudre le problme des huit reines, et celui des sous-suites. Enn, nous terminerons par une application aux jeux de stratgie deux joueurs en prsentant la stratgie MinMax et son amlioration par la mthode de coupure .

24.1

CRITURE RCURSIVE

Lcriture rcursive de lalgorithme de rtro-parcours est base sur une procdure dessai qui tente dtendre une solution partielle correcte ltape i. chaque tentative dextension de la solution partielle, un nouveau candidat est choisi dans une liste de candidats potentiels.

344

Chapitre 24 Algorithmes de rtro-parcours

La rcursivit permet les retours en arrire lors du parcours de larbre. Lorsque la solution partielle est invalide, et lensemble des possibilits ltape i est puis, lachvement de la procdure permet de revenir ltape prcdente. Cette premire version recherche une solution particulire, la premire.
Algorithme essayer(i, correcte) {Antcdent : la solution partielle jusqu ltape i-1 est correcte} {Consquent : correcte = solution partielle ltape i valide ou pas} {Rle : essaye dtendre la solution au ime coup} {initialisation de la liste des possibilits} k {candidat initial} rpter {prendre le prochain candidat dans la liste des possibilits} k {candidat suivant} {vrifier si la solution partielle ltape i est correcte} vrifier(i,ok) si ok alors enregistrer(i,k) si non fini(i) alors essayer(i+1,correcte) si non correcte alors {le choix k la ime tape conduit une impasse} annuler(i,k) finsi sinon {on a trouv une solution} correcte vrai finsi finsi jusqu correcte ou plus de candidats

Dans cet algorithme, la fonction ni teste si la dernire tape est atteinte ou pas, la fonction vrier teste la validit de la solution partielle ltape i, la procdure enregistrer mmorise dans la solution partielle le candidat k ltape i, et la procdure annuler efface de la solution partielle le candidat k ltape i. Pour obtenir toutes les solutions du problme, il suft, chaque tape, dessayer tous les candidats possibles. Remarquez que cela correspond au parcours complet de larbre de dcisions.
Algorithme essayer(i) {Antcdent : la solution partielle jusqu ltape i-1 est correcte} {Rle : essayer dtendre la solution au ime coup} {initialisation de la liste des possibilits} pourtout k de la liste des candidats faire {prendre le ke candidat dans la liste des possibilits vrifier si la solution partielle ltape i est correcte} vrifier(i,ok)

24.2

Le problme des huit reines

345

si ok alors enregistrer(i,k) si non fini(i) alors essayer(i+1) sinon {on a trouv une solution} criresolution finsi annuler(i,k) finsi finpour

24.2

LE PROBLME DES HUIT REINES

Ce problme, propos par C. F. G AUSS en 1850, consiste placer huit reines sur un chiquier sans quelles puissent se mettre en chec mutuellement (selon les rgles de dplacement des reines). Il ny a pas dalgorithme direct donnant une solution et un algorithme de rtroparcours doit tre utilis. Nous connaissons lalgorithme, nous allons nous intresser aux structures de donnes. Il est vident quon ne pourra placer quune reine par colonne. Pour chaque colonne c, le choix se rduit donc la ligne l sur laquelle poser la reine. partir de ce qui vient dtre dit, il est inutile de reprsenter lchiquier par une matrice 8 8, un tableau de huit positions suft pour reprsenter une solution :
solution type tableau [ [1,8] ] de [1,8]

Lalgorithme va placer une reine par colonne. Vrier si une reine est correctement place ncessite de vrier sil ny a pas de conit sur la ligne et sur les deux diagonales. Cette vrication doit rester simple. Nous utiliserons trois tableaux de boolens, ligne, diag1 et diag2 tels que pour une ligne l et une colonne c : ligne[l] indique si la ligne l est libre ou non ; diag1[k] indique si la k e diagonale est libre ou non ; diag2[k] indique si la k e diagonale est libre ou non. La gure 24.1 montre bien quoi correspondent diag1 et diag2 et comment reprsenter k en fonction de l et c. On voit donc qu partir de la position (l, c), la diagonale correspond lindice l + c et la diagonale lindice l c. Puisque l et c varient de 1 8, nous pouvons en dduire les dclarations de nos trois tableaux :
ligne type tableau [ [1,8] ] de boolen diag1 type tableau [ [12,16] ] de boolen diag2 type tableau [ [-7,7] ] de boolen

Pour enregistrer une reine en position (l, c), il suft dcrire :


solution[c] l ligne[l] diag1[l+c] diag2[l-c] faux

346

Chapitre 24 Algorithmes de rtro-parcours

l+c

lc

F IG . 24.1 Les deux diagonales issues de la position (l,c).

Et pour annuler une reine qui tait en position (l, c) :


ligne[l] diag1[l+c] diag2[l-c] vrai

Enn, vrier si la position (l, c) est correcte devient vident :


ligne[l] et diag1[l+c] et diag2[l-c]

Nous pouvons maintenant crire en JAVA la solution complte qui donne les quatre-vingt douze solutions de ce problme. Les dclarations des quatre tableaux sont les suivantes :
int [] solution = new int[8]; boolean [] ligne = new boolean[8]; boolean [] diag1 = new boolean[15]; boolean [] diag2 = new boolean[15];

Comme lindice du premier lment de ces tableaux est toujours gal zro, le calcul dindice pour accder aux composants devra subir une translation gale 1 pour les tableaux solution et ligne, gale 2 pour le tableau diag1, et gale +7 pour le tableau diag2. La mthode essayer scrit :
// Antcdent : c-1 reines ont correctement t places sur les // c-1 premires colonnes // Rle : essayer de placer la ce reine dans la ce colonne void essayer(int c) { int i; for (int l=1; l<=8; l++) { // vrifier si on peut placer la ce reine en (l,c) if (ligne[l-1] && diag1[l+c-2] && diag2[l-c+7]) { // enregistrer la ce reine en (l,c) solution[c-1]=l; ligne[l-1] = diag1[l+c-2] = diag2[l-c+7] = false; if (c==8) // on a plac la huitime reine System.out.println(this); else essayer(c+1);

24.3

criture itrative

347

// annuler la dernire reine ligne[l-1] = diag1[l+c-2] = diag2[l-c+7] = true; } } } // fin essayer

24.3

CRITURE ITRATIVE

Avec lcriture itrative des algorithmes de rtro-parcours, il nest plus possible, de fait, dutisliser les retours dappels rcursifs pour remonter dans larbre de dcision. Cette version de lalgorithme sappuie sur une procdure rgresser dont le rle consiste trouver ltape i laquelle un nouveau candidat peut tre propos. Si une telle tape ne peut tre trouve, elle signale une impasse. Son algorithme sexprime plus formellement :
Algorithme rgresser(i, impasse) impasse faux tantque candidat ltape i = dernier candidat possible faire i i-1 fintantque si i=0 alors impasse vrai sinon passer au candidat suivant de ltape i finsi

Lalgorithme itratif de rtro-parcours qui recherche une solution possible est donn cidessous :
initialisation de la solution partielle vide initialisation des diffrentes possibilits impasse faux tape 0 rpter {passer ltape suivante} i i+1 tendre(i) vrifier(i,correcte) tantque non (correcte ou impasse) faire rgresser(i,impasse) si non impasse alors vrifier(i,correcte) finsi fintantque {impasse ou solution ltape i correcte} jusqu impasse ou fini(i) si impasse alors pas de solution sinon on a trouv une solution particulire finsi

348

Chapitre 24 Algorithmes de rtro-parcours

Pour obtenir toutes les solutions possibles, lalgorithme doit poursuivre son parcours de larbre de dcision, en forant la rgression aprs la dcouverte dune solution. Le parcours sachve lorsque limpasse nale est atteinte. Lalgorithme itratif qui donne toutes les solutions est le suivant :
initialisation de la solution partielle vide initialisation des diffrentes possibilits impasse faux i 0 correcte vrai rpter {la solution partielle ltape i est correcte} si correcte alors si fini(i) alors {on a trouv une solution} crireSolution rgresser(i,impasse) sinon i i+1 tendre(i) finsi sinon {la solution partielle nest pas correcte} rgresser(i,impasse); finsi si non impasse alors vrifier(i,correcte) finsi jusqu impasse

24.4

PROBLME DES SOUS-SUITES

Ce problme consiste construire une suite de n lments, pris dans un ensemble de m valeurs, telle que deux sous-suites adjacentes ne soient jamais gales. Par exemple, < 1 2 1 3 > et < 2 3 2 1 > sont de telles suites de longueur 4 construites sur lensemble {1 2 3}. Ce problme est rsolu avec un algorithme de rtro-parcours, et nous donnerons sa version itrative. Les valeurs des lments de la suite sont des entiers positifs infrieurs une valeur valeurMax et la longueur de la suite est gale longSuite. Le nombre dtapes pour atteindre une solution est donc au plus gale longSuite. La solution est reprsente par un tableau dentiers et le numro de ltape courante sert dindice pour accder au dernier candidat de la solution partielle.
protected int[] solution; int i;

La mthode rgresser dcrmente la valeur dtape i tant quelle ne peut proposer de nouveau candidat cette tape. Lorsque i est gal zro, limpasse est atteinte.
private boolean rgresser() { while (i!=0 && solution[i-1]==valeurMax) i--;

24.4

Problme des sous-suites

349

if (i==0) return true; solution[i-1]++; return false; }

La vrication de la validit de la solution partielle, cest--dire vrier si la suite ne comporte pas deux sous-suites identiques, consiste tester toutes les sous-suites de longueur gale un jusqu la moiti de la longueur et faisant intervenir le candidat choisi ltape i 1. La gure 24.2 montre la progression de la taille des sous-suites vries en partant de la n de la suite. 3 3 3

2 1

ime tape
F IG . 24.2 Vrication de la validit de la suite.

La mthode vrifier est programme en JAVA comme suit :


private boolean vrifier(int longueur) { int lgCourante=0, // longueur de la sous-suite courante moiti=longueur/2; boolean diff=true; while (diff && lgCourante < moiti) { // les sous-suites de longueurs 0 lgCourante sont diffrentes int i=1; lgCourante++; // comparer deux sous-suites de longueur lgCourante do { diff = solution[longueur-i]!=solution[longueur-lgCourante-i]; i++; } while (!diff && i!=lgCourante); // les sous-suites de longueur lgCourante sont diffrentes // ou bien elles sont identiques et i>lgCourante } return diff; }

350

Chapitre 24 Algorithmes de rtro-parcours

Enn, la mthode solution qui cherche une solution particulire du problme des soussuites est programme comme suit :
public boolean solution() { boolean impasse=false; i=0; do { // tendre la solution solution[i++]=1; boolean correcte=vrifier(i); while (!(correcte || impasse)) { impasse=rgresser(); if (!impasse) correcte=vrifier(i); } } while (!impasse && i==longSuite); return !impasse; }

24.5

JEUX DE STRATGIE

Les jeux dchecs, de dames, ou encore le trictrac1 sont des jeux de stratgie deux joueurs. La programmation de ces jeux lorsque les deux joueurs sont des humains ne prsentent gure dintrt, puisque le programme se borne essentiellement la vrication de la validit des coups jous. En revanche, elle devient plus intressante, lorsquil sagit de faire jouer un utilisateur humain contre lordinateur. Au cours dun jeu, lordinateur et le joueur humain doivent, tour de rle, jouer un coup, le meilleur possible, cest--dire celui qui conduit la victoire nale, ou au moins une partie nulle. La stratgie du joueur humain est base sur son exprience du jeu ou son intuition. En gnral, elle tente dimaginer une situation de jeu plusieurs coups lavance en tenant compte des ripostes possibles de ladversaire. La stratgie de lordinateur est semblable, mais exhaustive. Elle consiste, chaque tape du jeu, essayer (rcursivement) tous les coups possibles, alternativement de lordinateur et dun joueur adverse virtuel, en ne considrant chaque fois que les meilleurs coups de chaque camp. Cette stratgie de jeu est appele stratgie MinMax2 et nous allons voir comment lordinateur la met en uvre pour jouer son meilleur prochain coup.

24.5.1

Stratgie MinMax

Prcisons, tout dabord, que cette stratgie ne sapplique quaux jeux qui sachvent aprs un nombre ni de coups, et chaque tape, un joueur a le choix entre un nombre ni de coups possibles. Pour chaque coup, la stratgie MinMax dveloppe un arbre, appel arbre de jeu, qui contient toutes les parties possibles partir dune position de jeu donne. Chaque feuille de
1 La

version franaise du backgammon. par O. M ORGENSTERN et J. VON N EUMANN en 1945.

2 Propose

24.5

Jeux de stratgie

351

cet arbre correspond un coup nal dune partie ctive, et laquelle sont associes trois valeurs possibles : partie gagne, nulle ou perdue. Les nuds de larbre correspondent, soit un coup jou par lordinateur, soit par son adversaire virtuel, et chacun deux contient une valeur qui reprsente le meilleur coup jou (du point de vue de lordinateur). La stratgie MinMax doit son nom au fait quelle cherche maximiser la valeur des coups jous par lordinateur et minimiser celle des coups jous par ladversaire virtuel. Lorsque lordinateur joue, il value rcursivement selon la stratgie MinMax tous les coups possibles pour ne retenir que le meilleur. Lorsque son adversaire virtuel joue, la stratgie de lordinateur est de retenir le moins bon coup. Il est, par exemple, vident quun coup perdant pour ladversaire, est conserver puisquil conduit la victoire de lordinateur. Rciproquement, un coup gagnant pour ladversaire est liminer puisquil conduit la dfaite de lordinateur. Pour un nud donn de larbre de jeu, la stratgie MinMax renvoie la meilleure valeur pour lordinateur.
Max gagne

Min

nulle

perdue

gagne

Max

nulle

nulle

nulle

perdue

gagne

gagne

F IG . 24.3 Un arbre de jeu.

La stratgie MinMax correspond donc un parcours en profondeur dun arbre de jeu compos alternativement de niveaux Max et de niveaux Min (voir la gure 24.3). Les nuds des niveaux Max sont les coups de lordinateur dont les valeurs sont le maximum de celles de leurs ls. Les nuds des niveaux Min sont les coups de ladversaire virtuel dont les valeurs sont le minimum de celles de leurs ls. La racine de larbre de jeu est situe un niveau Max, dont la valeur est le meilleur coup jouer par lordinateur. Lalgorithme suivant exprime ce parcours darbre.
Algorithme MinMax(a : arbre de jeu) {Rle : parcourt en profondeur larbre de jeu a et renvoie la valeur du meilleur coup jouer par lordinateur} si feuille(a) alors {coup final} rendre valeur(a) {i.e. gagne, nulle ou perdue} sinon si typeNoeud(a)=ordinateur alors {choisir la valeur maximale des fils} max Perdue

352

Chapitre 24 Algorithmes de rtro-parcours

pourtout i de 1 longueur(fort(a)) faire v MinMax(imeArbre(fort(a),i)) si v > max alors max v finsi finpour rendre max sinon {ladversaire virtuel} {choisir la valeur minimale des fils} min Gagne pourtout i de 1 longueur(fort(a)) faire v MinMax(imeArbre(fort(a),i)) si v < min alors min v finsi finpour rendre min finsi finsi

Il est important de bien comprendre que les programmes, qui mettent en uvre cette mthode, ne construisent pas au pralable les arbres de jeu parcourir. Ce sont les rgles du jeu et la faon dobtenir la liste des coups possibles chaque tape qui dterminent le parcours dun arbre de jeu implicite. Nous donnons maintenant la programmation en JAVA de lalgorithme prcdent. Nous considrerons quun coup jouer, de type Coup, est form dune position dans le jeu et dune valeur. La position est, par exemple, une case dun damier ou dun chiquier, et la valeur dun coup est prise dans lensemble ordonn {Perdue, Nulle, Gagne}. Notez quil est galement possible de complter, si ncessaire, ce type par la valeur dun pion (e.g. un cavalier noir ou une tour blanche aux checs). Nous dnissons galement un objet jeu qui permet denregistrer ou dannuler un coup, de renvoyer une numration des positions libres, dindiquer si un coup est gagnant ou non, ou encore si la partie est dans une situation de nulle ou non. La mthode MinMax donne ci-dessous tient compte de la symtrie de la mthode de jeu, ce qui permet de supprimer le test sur la nature du nud courant. Pour cela, il suft dinverser la valeur du meilleur coup du joueur adverse.
// Antcdent : le coup final nest pas encore trouv // Consquent : le meilleur coup jouer est renvoy Coup MinMax() { Coup meilleurCoup = new Coup(Perdue); numration posLibre = jeu.positionLibres(); // essayer tous les coups disponibles possibles do { Coup coup = new Coup(Perdue, positionLibre.suivante()); jeu.enregister(coup); // vrifier le coup if (jeu.coupGagnant(coup)) coup.valeur=Gagne;

24.5

Jeux de stratgie

353

else if (jeu.partieNulle()) coup.valeur=Nulle; else { // la partie nest pas termine // calculer le meilleur coup de ladversaire Coup coupAdversaire=MinMax(); coup.valeur=inverserValeur(coupAdversaire.valeur); } // est-ce un meilleur coup jouer ? if (coup.valeur>meilleurCoup.valeur) // ce coup est meilleur le conserver meilleurCoup=coup; jeu.annuler(coup); } while (!posLibres.finnumration()); // on a trouv le meilleur coup jouer return meilleurCoup; } // fin MinMax

Pour la plupart des jeux, le nombre de coups tests est trs important. Pour un jeu simple comme le tic-tac-toe3 (aussi appel morpion), le premier coup jou par lordinateur ncessite de parcourir un arbre de 29 633 nuds si le joueur humain joue son premier coup au centre de la grille, de 31 973 nuds si son premier coup est dans un coin, et de 34 313 nuds pour une autre case de la grille. Si lordinateur joue le premier, larbre de jeu initial possde 294 778 nuds quil devra parcourir avant de jouer son premier coup4 . Une premire amlioration vidente de lalgorithme prcdent est darrter le parcours des ls dun nud lorsque la valeur maximale attendue par le meilleur coup est atteinte. Le prdicat dachvement de lnonc itratif est simplement modi comme suit :
do { ... } while (!posLibres.finnumration() && meilleurCoup.valeur!=Gagne);

Avec cette modication, certains sous-arbres des arbres de jeu ne sont plus parcourus. On dit que ces sous-arbres sont coups ou lagus. Dans le cas du tic-tac-toe, le premier coup jou par lordinateur ne ncessite plus que le parcours de 4 867 nuds si le joueur humain joue son premier coup au centre de la grille, entre 2 210 et 4 872 nuds pour les coins, et entre 7 211 et 11 172 nuds pour les autres cases. Enn, lorsque lordinateur joue le premier, 56 122 nuds de larbre de jeu sont visits pour son premier coup.

24.5.2

Coupure

La mthode de coupure permet un lagage encore plus important de larbre de jeu, et offre une nette amlioration de lalgorithme prcdent pour un rsultat identique. Considrons larbre de jeu donn par la gure 24.4. Les cercles contiennent des valeurs attribues par lalgorithme aux nuds dj visits. Reste parcourir le sous-arbre marqu
3 Deux joueurs placent, alternativement, un cercle ou une croix dans les cases dune grille 3 3. Le premier aligner horizontalement, verticalement ou en diagonale, trois cercles ou trois croix a gagn. 4 En fait, lordinateur pourrait choisir au hasard nimporte quelle premire case. Elles sont toutes quivalentes pour le premier coup.

354

Chapitre 24 Algorithmes de rtro-parcours

dun point dinterrogation. Montrons que son parcours est inutile ! Sa racine est un niveau Min o lalgorithme minimise la valeur des nuds (coups de ladversaire virtuel). Sa valeur est infrieure celles des nuds dj valus au mme niveau, et ne sera donc pas retenue par la racine de larbre de jeu qui prend la valeur maximale de ses ls. Quelle que soit la valeur obtenue par le parcours du dernier sous-arbre, celle-ci ne sera pas prise en compte. En effet, si elle suprieure sa racine, elle est limine puisque sa racine conserve la valeur minimale. Si elle lui est infrieure, elle devient la nouvelle valeur de sa racine, mais demeure infrieure aux valeurs des nuds du mme niveau. Le parcours du dernier sous-arbre est donc superu. Llimination de ce sous-arbre dans lalgorithme MinMax est appele coupure . La valeur de coupure dun nud n dun niveau Min est gale la plus grande valeur connue de tous les nuds du niveau Max prcdent. Si le nud n possde une valeur infrieure , alors le parcours de ses sous-arbres non parcourus est inutile.
Max nulle coupure Min nulle perdue

Max

perdue

F IG . 24.4 Coupure .

De faon symtrique, la gure 24.5 montre une coupure dite . La valeur de coupure dun nud n dun niveau Max est gale la plus petite valeur connue de tous les nuds du niveau Min prcdent. Si le nud n possde une valeur suprieure , alors le parcours de ses sous-arbres non parcourus est inutile.
Min nulle coupure Max nulle gagne

Min

gagne

F IG . 24.5 Coupure .

24.5

Jeux de stratgie

355

Pour mettre en uvre la coupure , on ajoute simplement deux paramtres et MinMax. Lors des appels rcursifs, la valeur maximale connue du nud ordinateur est la valeur transmise, et la valeur minimale connue du nud adverse est la valeur transmise.
Algorithme MinMax(a : arbre de jeu, , ) {Rle : parcourt en profondeur larbre de jeu a et retourne la valeur du meilleur coup jouer par lordinateur} si feuille(a) alors {coup final} rendre valeur(a) {i.e. gagne, nulle ou perdue} sinon si typeNoeud(a) = ordinateur alors {choisir la valeur maximale des fils} max i 0 rpter i i+1 v MinMax(imeArbre(fort(a),i), max, ) si v > max alors max v finsi jusqu i = longueur(fort(a)) ou max rendre max sinon {adversaire virtuel} {choisir la valeur minimale des fils} min i 0 rpter i i+1 v MinMax(imeArbre(fort(a),i), , min) si v < min alors min v finsi jusqu i=longueur(fort(a)) ou min rendre min finsi finsi

Dans la mthode MinMax donne ci-dessous, on conserve la symtrie en confondant les paramtres et en un seul dont on inverse la valeur lors de lappel rcursif.
// Antcdent : le coup final nest pas encore trouv // Consquent : le meilleur coup jouer est renvoy Coup MinMax(Valeur alphabta) { Coup meilleurCoup = new Coup(Perdue); numration posLibre = jeu.positionLibres(); // essayer tous les coups disponibles possibles do {

356

Chapitre 24 Algorithmes de rtro-parcours

Coup coup = new Coup(Perdue, positionLibre.suivante()); jeu.enregister(coup); // vrifier le coup if (jeu.coupGagnant(coup)) coup.valeur=Gagne; else if (jeu.partieNulle()) coup.valeur=Nulle; else { // la partie nest pas termine // calculer le meilleur coup de ladversaire Coup coupAdversaire = MinMax(inverserValeur(meilleurCoup.valeur)); coup.valeur=inverserValeur(coupAdversaire.valeur); } // est-ce le meilleur coup jouer ? if (coup.valeur>meilleurCoup.valeur) // ce coup est meilleur le conserver meilleurCoup=coup; jeu.annuler(coup); } while (!posLibres.finnumration() && meilleurCoup.valeur<alphabta); // on a trouv le meilleur coup jouer return meilleurCoup; } // fin MinMax

Il a t montr que cette technique de coupure limite en pratique le nombre de nuds visits la racine carre du nombre de nuds de larbre de jeu. Notez que les coupures de larbre sont dautant plus importantes quun meilleur coup est trouv rapidement, cest-dire dans les sous-arbres les plus gauche. Pour le jeu du tic-tac-toe, le premier coup jou par lordinateur ne ncessite plus que le parcours de 1 453 nuds si le joueur humain joue son premier coup au centre de la grille, entre 1 324 et 2 345 nuds pour les quatre coins, et entre 1 725 et 3 508 nuds pour les autres cases. Enn, lorsque lordinateur joue le premier, 12 697 nuds de larbre de jeu sont visits pour son premier coup.

24.5.3

Profondeur de larbre de jeu

Les ordinateurs actuels sont capables de parcourir en une fraction de seconde larbre de jeu le plus grand du tic-tac-toe. Mais pour dautres jeux, comme les checs par exemple, le nombre de nuds et la profondeur des arbres, cest--dire celle de la rcursivit, sont tels quun parcours jusquaux feuilles nest pas praticable, mme avec des coupures . Pour une partie dchecs denviron 40 coups, avec en moyenne 35 possibilits par coup, il y aurait 3580 coups tester ! Notez galement que la limitation de la profondeur des arbres de jeu peut tre ncessaire avec des jeux plus simples, mais qui font intervenir plus de deux joueurs, puisque lalgorithme devra tester tous les coups des diffrents adversaires virtuels. Pour ces jeux, le parcours de larbre est arrt un niveau de profondeur xe par la puissance de lordinateur utilis (taille de la mmoire centrale et rapidit du processeur). Les nuds traits cette profondeur sont considrs comme des feuilles et leur valeur est calcule par une fonction qui estime ltat de la partie ce moment-l.

24.6

Exercices

357

Algorithme MinMax(a : arbre de jeu, , , niveau) si niveau = 0 alors {profondeur maximale atteinte} rendre une estimation de la partie en cours sinon si feuille(a) alors ... sinon {a est un noeud ordinateur ou de son adversaire virtuel} si typeNoeud(a) = ordinateur alors ... v MinMax(imeArbre(fort(a),i), max, , niveau-1) ... sinon {adversaire virtuel} ... v MinMax(imeArbre(fort(a),i), , min, niveau-1) ... finsi finsi finsi

Pour conclure, on peut dire que la stratgie MinMax assure ncessairement le nul ou la victoire lordinateur, si le fait de dbuter la partie ne donne pas un avantage irrversible son adversaire humain. Mais, pour la plupart des jeux, comme les checs ou les dames, larbre de jeu parcourir est trop grand et la qualit de la fonction qui estime la valeur du coup une profondeur xe devient essentielle.

24.6

EXERCICES

Exercice 24.1. crivez de faon itrative le programme des huit reines. Exercice 24.2. crivez un programme qui vrie sil est possible de passer par toutes les cases dun chiquier, mais une seule fois par case, laide dun cavalier selon sa rgle de dplacement aux checs. Exercice 24.3. Modiez le programme prcdent pour quil recherche toutes les solutions. Exercice 24.4. Le jeu Le Compte est bon de la clbre mission tlvise Des chiffres et des lettres consiste obtenir un nombre tir au hasard entre 100 et 999 partir des quatre oprations lmentaires (+, , , ) portant sur des entiers naturels tirs au hasard parmi vingt-quatre nombres (deux sries de nombres de 1 10, et 25, 50, 75 et 100). En utilisant lalgorithme de rtro-parcours rcursif, crivez un programme JAVA qui afche une suite doprations, si elle existe, ncessaires au calcul du nombre recherch. Exercice 24.5. Soit un entier m et un ensemble de n entiers E = {x1 , x2 , . . . , xn }, cherchez un sous-ensemble de E tel que la somme de ses lments soit gale m.

358

Chapitre 24 Algorithmes de rtro-parcours

Exercice 24.6. Soit une tle forme dun ensemble de carrs identiques et adjacents. On dsire dcouper dans cette tle un certain nombre de pices identiques. Le dcoupage doit se faire sans aucune perte et en suivant uniquement les cts des carrs qui composent la tle. Construire un programme qui imprime, si elle existe, une solution de dcoupage. Les donnes du programme doivent exprimer les formes et les dimensions de la tle et des pices. La gure 24.6 montre un exemple de tle avec trois pices dcouper.
tle pice 1

pice 2 pice 3

F IG . 24.6 Une tle et ses pices.

Exercice 24.7. Un labyrinthe est construit dans un carr nn. Il sagit de trouver un chemin dans le labyrinthe qui amne dun point de dpart un point darrive. La gure 24.7 montre un labyrinthe particulier dans un carr 5 5.

5 4 3 2 1
dpart arrive

F IG . 24.7 Un labyrinthe.

Proposez une reprsentation pour le labyrinthe, puis en utilisant un algorithme de rtroparcours, crivez un programme qui renvoie, sil existe, le chemin entre le point de dpart et le point darrive.

24.6

Exercices

359

Exercice 24.8. Utilisez et comparez les algorithmes A* ou IDA* avec lalgorithme de rtroparcours que vous avez programm prcdemment pour trouver votre chemin dans le labyrinthe. Exercice 24.9. Programmez le jeu du tic-tac-toe. Quelle est la pronfondeur la plus grande dun arbre de jeu ? Exercice 24.10. Le jeu du 31 se pratique deux joueurs et un d. tour de rle, les joueurs font progresser une somme partielle par additions successives de la valeur du d. Au dbut, le d est lanc au hasard, et la somme partielle est initialise zro. Ensuite, chaque joueur tourne dun quart de tour le d sur une des quatre faces adjacentes celle visible du coup prcdent. La nouvelle valeur du d est ajoute la somme partielle. Le premier joueur qui atteint la somme 31 a gagn, celui qui dpasse 31 a perdu. Programmez ce jeu. Exercice 24.11. Le jeu hexxagon se droule sur un plateau de jeu hexagonal comprenant un nombre quadratique de cases (voir gure 24.8). Au dbut de la partie, chaque joueur dispose de deux pions dune couleur donne. Les joueurs jouent tour de rle. Deux types de dplacement sont possibles : le clonage : un pion peut se dupliquer sur lune de ses six cases adjacentes condition quelle soit libre, le saut : un pion peut sauter deux cases de distance.

(a)

(b)

(c)

(d)

(e)

(f)

F IG . 24.8 (a) Situation initiale ; (b) clonage ; (c) clonage ; (d) saut ; (e) clonage avec prise ; (f) saut avec prise.

Lorsquun pion est pos sur une case, tous les pions prsents sur les six cases adjacentes prennent la couleur du pion dplac. Le jeu se termine lorsquil ny a plus de cases libres sur le plateau. Le vainqueur est le joueur qui possde le plus de pions de sa couleur. Programmez ce jeu pour deux joueurs, lordinateur et un joueur humain, puis pour quatre joueurs, lordinateur et trois joueurs humains.

Chapitre 25

Interfaces graphiques

Les applications interactives communiquent avec lutilisateur au moyen dinterfaces. Parmi elles, les interfaces graphiques ont rvolutionn les mthodes de dialogue avec lordinateur et ont simpli son utilisation. Ce type dinterface a t labor ds la n des annes 60 dans les laboratoires de la compagnie X EROX, mais cest vraiment au dbut des annes 80 que linterface purement graphique du systme du M ACINSTOSH a donn laccs lordinateur au plus grand nombre, et en particulier aux non informaticiens. Aujourdhui, la grande majorit des systmes dexploitation et des applications, et particulirement dans linformatique individuelle, dispose dune interface graphique. Aprs avoir dcrit la notion de systme interactif, nous nous intresserons plus particulirement dans ce chapitre aux interfaces graphiques. Nous prsenterons les caractristiques principales des systmes de fentrage, celles des fentres quils manipulent et des outils de construction dinterfaces graphiques. Enn, travers quelques exemples simples, nous verrons les principes de base de la programmation des applications graphiques en JAVA.

25.1

SYSTMES INTERACTIFS

Une interface utilisateur dsigne la fois lquipement matriel et les outils logiciels qui permettent lutilisateur dassurer une communication avec lordinateur. Dans le pass, les ordinateurs enchanaient lexcution des programmes sans attente. Le mode de communication tait le traitement par lots (mode batch en anglais). Aujourdhui, la communication avec lordinateur est interactive, et lutilisateur instaure un vritable dialogue avec lordinateur. Dans ces systmes interactifs, on peut distinguer deux grands types dinterfaces : textuelles et graphiques.

362

Chapitre 25 Interfaces graphiques

Jusquau milieu des annes 80, linterface tait essentiellement textuelle. Les terminaux daccs aux systmes dexploitation taient presque exclusivement de type alphanumrique, et ne permettaient lafchage que dun nombre limit de lignes de caractres pris dans le jeu A SCII. Dun point de vue logiciel, ces terminaux noffraient donc que des interfaces textuelles, cest--dire que lutilisateur communiquait avec le systme uniquement par des commandes rdiges dans un langage donn et interprtes par un interprte de commandes. Les interprtes de commandes des systmes dexploitation, comme par exemple un shell du systme U NIX, fonctionnent selon ce principe. Lintrt de ce type dinterface est la richesse du langage de commandes qui permet de dnir ou dinventer des comportements non prvus. En revanche, lapprentissage du langage et la saisie des commandes sont certainement une source de difcults, particulirement pour les non informaticiens. Dautre part, les terminaux alphanumriques noffrent pas, en gnral, la possibilit de visualiser lexcution simultane de plusieurs tches. Bien que les interfaces exclusivement textuelles naient pas totalement disparu, aujourdhui linterface habituelle est graphique. Elle ncessite un terminal graphique, capable dafcher une matrice de points (de lordre de 2400 3200 pour les meilleurs) et un systme de fentrage laide duquel on dessine des caractres, au mme titre que des schmas, des images, etc. Un dispositif de pointage, en gnral une souris, est toujours associ lcran. Il permet de dsigner ou de manipuler des parties de limage graphique de lcran. Pour son dialogue avec lordinateur, lutilisateur se sert de la souris et accde aux fonctionnalits du systme grce des menus droulants ou des boutons spciques. Dplacer la souris permet damener le pointeur sur la zone dsire de lcran. Les combinaisons de clics simples ou clics doubles et de clics suivis dun dplacement avec son bouton enfonc permettent dindiquer des actions, ou des copier-coller . Le systme rpond en afchant des menus et en faisant apparatre des fentres, cest--dire des zones de lcran dans des cadres qui afchent une information particulire. Les interfaces graphiques exclusivement base de menus droulants, de boutons, ou de botes de dialogues facilitent linteraction, mais ont des limitations. Le plus souvent, elles ne permettent quune utilisation passive, avec un dialogue immuable x par lenchanement des menus et des botes de dialogues. Elles noffrent pas la possibilit de crer de nouveaux comportements par lextension ou la composition de comportements existants. Lutilisation pousse lextrme dune interface graphique dispense de lemploi du clavier, mais rapidement, on saperoit que lusage exclusif de la souris devient fastidieux, et le clavier reprend ses droits. En revanche, les interfaces manipulation directe, comme par exemple les diteurs de texte ou de schmas, offrent lutilisateur une reprsentation graphique des objets informatiques et en permettent une manipulation par lintermdiaire de la souris ou du clavier. Ce type dinterface permet une interaction cratrice qui nest plus limite par les rgles xes dutilisation des menus droulants ou des botes de dialogues. Les interfaces des ordinateurs actuels font essentiellement appel au sens visuel de lutilisateur (afchage sur lcran), mais dautres types dinterfaces permettent de combiner les autres sens et dnissent de nouveaux modes de communication entre lhomme et la machine. Grce, par exemple, des haut-parleurs, lutilisateur peut entendre lordinateur, ou lui parler laide dun micro. Il peut galement slectionner la main des objets sur des crans tactiles. Citons galement les systmes de ralit virtuelle qui permettent une interaction avec tous les sens de lutilisateur dans une reprsentation 3D du monde. Il est ais

25.2

Conception dune application interactive

363

utilisateur
entres retours sorties

interface utilisateur

commandes

rponses

oprations noyau fonctionnel structures de donnes internes

F IG . 25.1 Structure dune application interactive.

dimaginer toutes sortes de combinaisons dinteractions multimodales. Ces types dinterfaces sont pour linstant beaucoup moins frquentes que les interfaces simplement graphiques, mais vont sans aucun doute se dvelopper avec les progrs technologiques venir, et deviendront, peut-tre, les interfaces habituelles des systmes interactifs de demain.

25.2

CONCEPTION DUNE APPLICATION INTERACTIVE

Une application interactive est gnralement forme de deux composants principaux, linterface utilisateur et le noyau fonctionnel. Linterface permet le dialogue entre lutilisateur et le noyau de lapplication qui assure les fonctions de calcul. Lutilisateur applique des commandes laide de dispositifs dentre qui agissent sur le noyau fonctionnel. En retour, ce dernier produit des rponses grce des dispositifs de sortie. Les commandes de linterface utilisateur excutent des oprations internes au noyau fonctionnel qui agissent sur ses propres structures de donnes. Linterface offre donc lutilisateur une reprsentation des structures internes de lapplication. Notez que certaines commandes de linterface utilisateur peuvent provoquer des retours dinformation sans pour autant provoquer lexcution doprations du noyau. La gure 25.1 montre cette structure. Pour illustrer ces propos, prenons lexemple dune interface utilisateur dun logiciel de jeu dchecs qui visualise graphiquement sur lcran lchiquier et les pices sous forme dicnes. Ses commandes, comme le dplacement dune pice sur lchiquier laide dune souris, provoqueront lexcution des oprations du noyau fonctionnel de contrle de validit du coup jou, dexcution du coup suivant selon un algorithme de coupure -, etc. Les

364

Chapitre 25 Interfaces graphiques

rponses associes aux coups jous par lordinateur, ou aux prises de pices produiront en sortie des dplacements ou des suppressions de pices lcran. Dans ce logiciel, un retour dinformation dune commande sera, par exemple, un changement dapparence dune pice dplace ou de son suivi lcran lors du dplacement, qui ne provoquera pas lexcution dune opration du noyau. Cette dernire ne sera, en fait, excute que lorsque la pice sera effectivement pose par le joueur sur une case de lchiquier. Durant les deux dernires dcennies, plusieurs modles darchitecture logicielle ont t proposs pour structurer les interfaces des systmes interactifs. Ils visent tous une sparation claire entre linterface utilisateur et le noyau fonctionnel an de faciliter la construction des systmes interactifs. Le modle de S EEHEIM1 [Pfa85] repose sur un modle linguistique (lexical, syntaxique, smantique) du dialogue homme-machine. Il dnit linterface utilisateur comme un module distinct form de trois composants (voir la gure 25.2). Le premier appel prsentation, le composant lexical, soccupe de la visualisation des objets graphiques ; le deuxime, le contrleur de dialogue, le composant syntaxique, dnit la structure de linteraction entre lutilisateur et le noyau fonctionnel ; enn, le troisime, appel interface, le composant smantique, tablit le lien avec les fonctionnalits du noyau. Ce dernier est aussi appel adaptateur du noyau fonctionnel.

Noyau fonctionnel

Interface

Contrleur de dialogue

Prsentation

Utilisateur

F IG . 25.2 Le modle de S EEHEIM.

La caractristique principale du modle langage est sa forme squentielle et centralise des traitements. Toutefois, elle ne correspond pas forcment au comportement des utilisateurs, en particulier avec des interfaces manipulation directe. Un autre modle important, le modle multi-agents organise larchitecture de linterface utilisateur autour dun ensemble dobjets interactifs rpartis. Plusieurs modles ont t construits selon ce principe. Le premier dentre eux, et le plus connu, est le modle MVC (Model-View-Controller) [KP88]. Dans ce modle, lapplication interactive est construite partir de modles (models), qui sont les composants logiciels internes du noyau fonctionnel. chaque modle, on peut associer un ou plusieurs couples View/Controller. Une vue (view) gre la visualisation du modle, et le contrleur (controller) assure linterface entre les modles et les vues, auxquels il est li, partir des entres de lutilisateur. Ainsi quand ce dernier ralise une action sur un dispositif dentre (clavier, souris...), le contrleur en informe le modle associ qui modie ses structures de donnes. Le modle notie en retour son changement dtat ses diffrents contrleurs et vues qui feront leurs propres changements dtats qui simposent (voir la gure 25.3). lorigine, le modle MVC a t mis en uvre dans le langage objets S MALLTALK [GR89]. On le retrouve aussi, sous des formes adaptes, dans dautres langages, dont JAVA.
1 Il

doit son nom au lieu de la confrence dans laquelle il a t prsent.

25.3

Environnements graphiques

365

modle

entres (clavier, souris...)

contrleur

vue

affichage (cran)

F IG . 25.3 Le modle MVC.

Ces premiers modles ont eu de nombreux successeurs comme le modle Arch [BFL+ 92] qui est une extension du modle de S EEHEIM, ou encore le modle PAC [Cou87] qui cherche combiner les proprits du modle langage et du modle multi-agents. Le lecteur intress pourra se reporter [BBL95] pour une classication des modles existants. Linterface utilisateur et le noyau fonctionnel sont deux parties dont les conceptions doivent tre menes, autant que faire se peut, simultanment pour que les concepteurs aient une vision globale du logiciel. Mais cela nest pas toujours possible, surtout quand il sagit de pourvoir dune interface un noyau fonctionnel existant. Les spcicits des utilisateurs, surtout si ceux-ci ne sont pas des informaticiens, sont aussi des points essentiels prendre en compte lors de la conception dun systme interactif. Les comptences, les prfrences et les gots, les possibilits humaines (oue, vision... mais aussi les handicaps) ou encore la psychologie des utilisateurs sont, au mme titre que les contraintes techniques du noyau fonctionnel, des caractristiques fondamentales dont le ou les concepteurs doivent tenir compte pour le dveloppement de linterface.

25.3

ENVIRONNEMENTS GRAPHIQUES

Les applications qui possdent une interface graphique sexcutent sur des ordinateurs dont le systme dexploitation est pourvu dun systme de fentrage. Dans cette section, nous dcrirons tout dabord ce quest un systme de fentrage et la notion de fentre quil met en jeu. Nous prsenterons ensuite les principes des outils qui servent la construction des interfaces graphiques.

25.3.1

Systme de fentrage

Le systme de fentrage peut tre intgr dans le systme dexploitation, comme pour les systmes de M ICROSOFT, mais peut tre aussi un module spar et indpendant dun systme dexploitation particulier, comme par exemple le systme de fentrage X2 bas sur la bibliothque Xlib. Le systme de fentrage gre lactivit dafchage dune part, et les ordres dafchage dautre part. Loriginalit du systme X, puisquon ne la retrouve dans aucun
2 Ce

systme de fentrage a t dvelopp lorigine, dans les annes 70, au MIT.

366

Chapitre 25 Interfaces graphiques

autre systme de fentrage, est de sparer ces deux activits selon une relation client-serveur. Les clients sont des applications qui assurent des calculs et qui produisent des requtes dafchage destination du serveur. Le serveur est un programme qui gre le dispositif physique form de lcran graphique, du clavier alphanumrique et de la souris. Il traite les ordres dafchage lcran et reconnat les vnements mis par le clavier ou la souris, et informe les clients concerns si ncessaire. Les vnements qui se produisent sur un terminal sont par exemple le dplacement de la souris, lappui sur une touche du clavier, ou un bouton de la souris, le recouvrement dune fentre par une autre, etc. Les clients et le serveur dafchage peuvent sexcuter sur le mme ordinateur ou sur des machines diffrentes communiquant alors par lintermdiaire du rseau selon les protocoles rseaux TCP/IP ou DECnet. La proprit fondamentale du systme X est dtre indpendant des ordinateurs utiliss. Seul le serveur X dpend du terminal quil gre. Ainsi, plusieurs clients X qui sexcutent sur des ordinateurs diffrents, et mme sous des systmes dexploitation diffrents, peuvent soumettre des requtes dafchage un mme serveur X de faon homogne. Les clients nont pas besoin de savoir comment fonctionne le serveur, et vice-versa. Clients et serveur respectent un protocole de communication unique et indpendant du matriel et des systmes dexploitation. Ce modle client/serveur de X possde de nombreux avantages. Il permet en particulier de rpartir la puissance de calcul sur plusieurs machines et de partager les ressources disponibles, alors que lafchage a lieu sur un terminal unique. Ce terminal peut tre un matriel de faible cot, alors que les clients sexcutent sur des machines trs puissantes et onreuses. Le systme de fentrage gre donc des fentres, cest--dire des zones de lcran dans des cadres, la plupart du temps rectangulaires, qui afchent une information particulire des applications. Il offre tout un ensemble de primitives qui permettent aux programmeurs dapplications interactives de crer ou de dtruire des fentres, ou encore den modier le contenu. Le systme de fentrage X ne permet pas la manipulation interactive des fentres par lutilisateur, par exemple, il ne prend pas en charge les dplacements des fentres ou leur agrandissement. Cest le travail dune application particulire, le gestionnaire de fentres, qui gre le dialogue avec lutilisateur et assure gnralement : le placement interactif des fentres ; le recouvrement et lempilement ; licnication ; le focus du clavier ; le changement de taille ; la dcoration des fentres ; les menus de commandes.

Lcran du terminal afche des fentres. Mme en labsence de toute fentre, il en existe toujours une, la fentre racine, qui correspond la totalit de lcran et afche une trame, une couleur, ou une image de fond. On lappelle racine car les fentres cres par la suite constituent une arborescence dont le fond dcran est la racine. Une des premires fonctions du gestionnaire de fentres est la mise en place interactive des fentres sur la fentre racine. Toute nouvelle fentre safche sur la fentre racine, et peut se trouver dans diffrents tats : visible, icnie, invisible, dtruite ; active ou inactive.

25.3

Environnements graphiques

367

Une fentre correspond un processus ; si elle est visible, les ordres dafchage envoys par le processus modient ce qui safche lcran. Une fentre dont on ne regarde plus le contenu occupera moins de place lcran si elle est icnie : dans ce cas elle est remplace par une icne, cest--dire une mini-fentre qui porte le mme nom, mais dont on ne peut pas voir le contenu. On peut galement la rendre compltement invisible, condition davoir un moyen de la faire rapparatre ultrieurement. Enn, une fentre peut tre dtruite. Le passage dans lun ou lautre de ces tats se fait par des ordres au gestionnaire de fentres, donns laide de la souris. Le placement des fentres permet le recouvrement et lempilement des fentres les unes sur les autres. Les gestionnaires de fentres proposent en gnral des mcanismes, pour faire passer en avant-plan ou en arrire-plan les fentres. Une seule fentre est active la fois, cest vers elle que tous les caractres frapps au clavier sont envoys. On dit que cette fentre a le focus. La manire de rendre une fentre active est lune des caractristiques ergonomiques importantes de toute interface graphique. Pour certains gestionnaires de fentres, une fentre devient active simplement si le pointeur de la souris se trouve dessus ; pour dautres, lutilisateur doit explicitement cliquer dans la fentre pour lui donner le focus. Bien souvent, les gestionnaires de fentres permettent lutilisateur de choisir lun ou lautre des comportements. Lagrandissement et le rtrcissement des fentres font aussi partie des tches du gestionnaire de fentres. Le changement de taille se fait avec la souris ; lutilisateur ajuste de faon interactive la taille de la fentre aux dimensions souhaites. Une autre possibilit est dagrandir la dimension de lcran la fentre, de telle faon quelle recouvre tout lcran en masquant toutes les autres fentres. Une fentre est normalement munie dun dcor, plac par le gestionnaire de fentres, qui peut servir dune part effectuer certaines actions sur la fentre. Un lment fondamental du dcor est la barre de titre, qui comporte quelques boutons de commande, le nom de la fentre, et qui permet galement de dplacer la fentre sur lcran. la fentre racine, on associe en gnral des menus, dont les entres permettent dexcuter des commandes. Contrairement linterface classique du systme du M ACINSTOSH, qui place en haut dcran une barre de menus dont le contenu dpend de la fentre active, on utilise en gnral avec X des menus surgissants, quon peut faire apparatre en cliquant nimporte o sur la fentre racine. Le minimum est den avoir un qui permet au moins de crer quelques nouvelles fentres, dappliquer certaines actions aux fentres existantes, et de terminer lexcution du gestionnaire de fentres.

25.3.2

Caractristiques des fentres

Une fentre peut tre caractrise par un certain nombre de proprits fondamentales, que lutilisateur peut paramtrer. Nous nous limiterons prsenter trois caractristiques de base : la gomtrie, la couleur et les polices de caractres. La gomtrie La gomtrie donne les dimensions de la fentre et sa position sur lcran. La largeur et la hauteur de la fentre sont mesures en gnral en pixels, qui correspondent aux points

368

Chapitre 25 Interfaces graphiques

lumineux de lcran, mais aussi en caractres pour certaines applications. La taille relle de la fentre dpend de la distance entre deux points lumineux, et plus prcisment de sa rsolution, cest--dire du nombre de pixels par ligne et par colonne. Plus la rsolution est grande, plus la fentre apparatra petite, et plus la prcision sera grande. La position dune fentre lcran se fait par un systme de coordonnes, spciant des distances par rapport un ou plusieurs repres. En gnral, le coin en haut gauche dune fentre possde la coordonne (0, 0). La couleur Les anciens crans graphiques tube cathodique, ou les crans graphiques LCD (Liquid Crystal Display) actuels comprennent en chaque pixel trois types de phosphore ou dlectrode qui, selon la technologie, mettent respectivement les couleurs rouge, vert et bleu. Laddition de ces trois couleurs avec la mme intensit donne du blanc, labsence de ces trois couleurs donne du noir. Les couleurs possibles sont notes par trois nombres, qui donnent la valeur de lintensit pour chacune des trois couleurs de base. Sur la plupart des crans actuels, une intensit varie entre 0 et 255, cest--dire quelle est reprsentable sur un octet, et quune valeur ncessite un nombre de 24 bits. Il existe donc potentiellement 224 couleurs diffrentes, cest--dire exactement 16 777 216. Le contenu de lcran est reprsent par une mmoire dcran, qui doit reprsenter la valeur de la couleur de chaque pixel. La taille de cette mmoire dcran aura donc une incidence directe sur le nombre de couleurs possibles. Pour un cran 1600 1280, la mmoire dcran devra tre de presque 6 Mo. Lorsque la mmoire dcran est de taille rduite3 , le systme de fentrage ne reprsente dans cette mmoire que un ou deux octets au lieu de trois, ce qui ne permet pas de coder directement la valeur de la couleur. Ce codage est fait de manire indirecte, par une table des couleurs associe la mmoire dcran, qui comprend 256 mots de 24 bits lorsquon fait un codage sur un seul octet. La valeur associe un pixel dans la mmoire dcran est donc en fait un indice dans la table des couleurs (voir la gure 25.4). On garde la possibilit des 16, 5 millions de couleurs, mais on ne peut en utiliser que 256 la fois. Un systme de fentrage permet en gnral de modier trs rapidement le contenu de la table des couleurs, ce qui permet den avoir une par fentre. Dans ce cas, le changement se fait avec le changement de focus, au dtriment des couleurs des autres fentres. Tout ce mcanisme constitue le modle RGB (Red - Green - Blue) des couleurs, qui est un modle additif assez intuitif. Les coloristes utilisent plus souvent un modle soustractif, plus proche de ce quon fait avec une bote de peinture, et qui utilise comme couleurs fondamentales le jaune, le pourpre et le bleu sombre : cest le modle YMC (Yellow - Magenta Cyan). Un autre modle encore utilis, le modle HIS (Hue - Intensity - Saturation), dnote une couleur par sa teinte qui un est un angle sur un cercle de couleurs possibles, son intensit, qui est une valeur entre le noir et le blanc, et sa saturation, qui va de labsence de couleur la couleur pure. Tous ces modles sont quivalents, et on a par exemple, les couleurs suivantes dans le modle RGB (notes en hexadcimal) :
3 Les

cartes graphiques actuelles proposent des tailles de mmoire toujours plus grandes.

25.3

Environnements graphiques

369

noir blanc rouge vert bleu jaune (rouge + vert) cyan (vert + bleu) magenta (rouge + bleu)

000000 FFFFFF FF0000 00FF00 0000FF FFFF00 00FFFF FF00FF

Quand on veut choisir une couleur, on peut le faire en donnant sa valeur numrique dans le modle RGB. Bien souvent, on dispose galement dune table de noms de couleur qui fournit des noms plus parlants : Black, Red, ou encore WhiteSmoke, MintCream, navy blue.
i 0 1 mmoire vido R V B 1 238 130 238 i table des couleurs (colormap) p n= nb de pixels

m = nombre de plans convertisseurs

point p violet

F IG . 25.4 Afchage dun point sur 1 octet.

Les polices de caractres Une police de caractres est un alphabet form de signes de mme style, forme, corps, etc. Tout texte afch sur lcran ncessite lutilisation dune ou plusieurs polices de caractres, puisque le simple fait de mettre un mot en gras ou en italique ncessite de changer de police. Une police est caractrise par un grand nombre dattributs. Par exemple, dans le systme X, on trouve les attributs suivants : le fournisseur de la police ; la famille ;

370

Chapitre 25 Interfaces graphiques

la graisse : maigre, mdium ou gras ; linclinaison : roman (droit), oblique, italique ; la largeur ; normal, large troit ; la taille en pixel (hauteur de la partie centrale des caractres) ; la taille en points ; la rsolution ; lespacement : xe ou proportionnel ; la largeur moyenne ; la norme de lalphabet ; la variante de la norme.

Il est important davoir du discernement dans le choix des polices, pour obtenir une bonne lisibilit tout en utilisant efcacement la surface de lcran.

25.3.3

Botes outils

La programmation dune interface graphique avec la bibliothque dun systme de fentrage, comme par exemple la bibliothque Xlib du systme X, est une tche rellement fastidieuse ; un peu comme programmer une grosse application en langage dassemblage. Pour sen convaincre, il suft de lire la documentation de cette bibliothque graphique. Pour faciliter la programmation des interfaces graphiques, les botes outils proposent des composants graphiques de haut niveau, appels widget (window object) et un ensemble de fonctions pour les manipuler. Les fonctions de base sont celles de cration et destruction, de placement, et de communication avec le noyau fonctionnel de lapplication. La construction de linterface graphique se fait par assemblage de widgets organiss de faon hirarchique. Dans toutes les botes outils, on retrouve des primitives de cration de widgets simples comme les boutons, les tiquettes, les ascenseurs et de widgets composs comme les menus, les botes de dialogues, les radio-boutons ou encore des widgets spcialement conus pour lassemblage de widgets simples ou composs. Dans bon nombre de botes outils, lassemblage des widgets se fait par lintermdiaire de widgets spcialiss selon plusieurs mthodes de placement. On en distingue en gnral trois qui permettent un placement des widgets de faon indpendante de leur taille effective. La premire consiste placer les widgets laide dun systme de coordonnes, horizontales et verticales, sur une grille. La seconde permet de positionner les widgets les uns par rapport aux autres. Les widgets sont empils ou juxtaposs dans des conteneurs verticaux ou horizontaux, ou encore placs relativement les uns par rapport aux autres laide de directives de type au-dessus, au-dessous, gauche ou droite. Enn, la troisime consiste spcier un ensemble de contraintes, dnies par des quations, que doivent vrier les widgets. Ce sont, par exemple, des contraintes de distance que doivent respecter les widgets entre eux. Notez que ce dernier type de placement, contrairement aux deux premiers, nexiste que dans peu de botes outils. Les widgets offrent leurs utilisateurs un look and feel, cest--dire une apparence et un comportement. Lapparence concerne, par exemple, la forme ou la couleur du composant graphique. Bien souvent, lapparence peut tre paramtre. Il sera alors possible de changer le texte inscrit sur une entre de menu ou de modier la taille dun bouton. Le comportement

25.3

Environnements graphiques

371

dun widget dnit son fonctionnement qui, en gnral, ne peut tre modi par le programmeur. Chaque widget possde un comportement qui lui est propre en raction aux vnements auxquels il est soumis. Ainsi, pour un bouton, le fait de cliquer dessus change son apparence graphique, et par un effet dombre donne limpression quil est enfonc. Contrairement aux applications des chapitres prcdents, dont lexcution suivait lordre squentiel et immuable dni par leur algorithme, les programmes contrls par une interface graphique sont dirigs par lutilisateur, et plus prcisment par les vnements auxquels ragissent les composants graphiques. On parle de programmation dirige par les vnements. Toutes les botes outils fournissent des mcanismes qui permettent aux widgets de linterface graphique de communiquer avec le noyau de lapplication. Il existe trois grands mcanismes : les fonctions de rappels, les vnements et les variables actives : Les fonctions de rappel (en anglais callbacks) sont le mcanisme le plus classique dont lintrt principal est la simplicit. Ces fonctions sont associes aux widgets lors de leur cration (ou bien par une primitive spciale). Leur programmation effectue des actions sur les structures de donnes du noyau, ou sur des widgets de linterface graphique, par lintermdiaire de paramtres xs par la bote outils. Ainsi, lorsquun widget est activ, par exemple lors dun clic sur un bouton ou sur une entre de menu, sa fonction de rappel est excute. Notez que la notion dvnement napparat pas explicitement dans ce mcanisme. Linconvnient des fonctions de rappel est de destructurer le code du programme. En effet, dans la mesure o les appels de ces fonctions ne sont pas explicites, leur texte peut tre plac nimporte o dans le programme, sans cadre syntaxique ou smantique spcique, ce qui, lorsque leur nombre devient important, entrane un manque de lisibilit du programme global. Le lecteur dsireux de mettre en uvre ce mcanisme pourra sexercer avec la bote outils libsx [Gia91] dont lutilisation est particulirement aise dans lenvironnement X. Les botes outils qui utilisent le mcanisme dvnement, comme par exemple AWT de JAVA, dnissent explicitement cette notion. La bote outils dnit aussi la notion dauditeurs qui sont des gestionnaires dvnements associs aux widgets. Les vnements sont reprsents par des objets qui sont crs lors de lactivation dun widget et mis destination de son auditeur associ. Les auditeurs contiennent les fonctions de rappel excuter lorsquils interceptent un vnement issu de composant graphique auquel ils sont lis. En gnral, lvnement est transmis en paramtre de la fonction de rappel excute. Lintrt de cette mthode par rapport la prcdente est de permettre une meilleure localisation du code des fonctions de rappel. Les variables actives sont des variables du noyau fonctionnel qui sont associes aux widgets. Lorsquune variable active change de valeur, ltat du composant graphique change pour reter la nouvelle valeur. Rciproquement, lorsque ltat du widget est modi, une nouvelle valeur sera affecte sa variable active exprimant le nouvel tat. Ce mcanisme est trs utilis dans la bote outils Tk avec le langage de script Tcl [Ous94].

25.3.4

Gnrateurs

Mme avec une bote outils, le dveloppement dune interface graphique reste une tche ardue. Les gnrateurs aident la conception et limplmentation des interfaces utilisateurs.

372

Chapitre 25 Interfaces graphiques

Les premiers gnrateurs ont t conus pour produire des interfaces partir dune spcication exprime sous forme textuelle dans une notation formelle (grammaires hors contexte, langages dclaratifs spcialiss, rseaux de Ptri, etc.) la connaissance de cette notation formelle devait sajouter celle du langage cible dcriture du systme interactif. Leurs successeurs construisent la spcication de linterface par manipulation directe, vitant ainsi au concepteur la connaissance et la programmation de la notation formelle. Ces gnrateurs deviennent eux aussi des outils graphiques et interactifs. Leurs diteurs permettent lutilisateur de placer facilement avec la souris les composants graphiques (boutons, menus, etc.) de la future interface. Sils facilitent la conception des interfaces par un assemblage interactif des objets graphiques, ce type de gnrateurs noffre en gnral quune aide limite quant la programmation de leur comportement dynamique et leurs liens avec le noyau fonctionnel de lapplication. La dernire gnration de ces systmes permet le dveloppement des applications par assemblage de composants logiciels du noyau de lapplication mme. Dans le monde JAVA, ces composants appels Beans4 ventuellement crits et compils sparment, sont assembls de faon interactive et graphique laide dune plate-forme dassemblage spcique comme, par exemple, Bean Builder ou plus rcemment NetBeans. Celles-ci offrent une palette de composants graphiques et des mcanismes dassemblage qui grent le comportement dynamiquement de lapplication.

25.4

INTERFACES GRAPHIQUES EN JAVA

La plupart des langages de programmation possde des bibliothques de composants graphiques prts lemploi pour construire des interfaces graphiques. Le langage JAVA en propose deux, AWT (Abstract Window Toolkits) et Swing. La seconde est construite partir de la premire et propose une version adapte du modle MVC. Les exemples qui suivent sont programms avec AWT. Leur but nest pas de faire une prsentation exhaustive de cette bote outils graphique, mais simplement dillustrer les notions prsentes dans les sections prcdentes.

25.4.1

Une simple fentre

Pour commencer, nous donnons ci-dessous le texte dune classe qui afche Bonjour tous dans une fentre graphique. Une fentre est une portion de lcran, en gnral, rectangulaire, qui possde sur sa partie suprieure une barre de titre.
import java.awt.*; public class FentreBonjour extends Frame { // les dimensions de la fentre static final int LARGEUR=300; static final int HAUTEUR=200;

4 Ces composants sont reprsents par des classes qui suivent des rgles spciques. Ces classes doivent, par exemple, respecter des conventions de nommages et tre serialisable pour la persistance.

25.4

Interfaces graphiques en Java

373

public FentreBonjour() { // mettre un titre la fentre super("Bonjour"); // fixer sa dimension setSize(LARGEUR,HAUTEUR); // la rendre visible setVisible(true); } public void paint(Graphics g) { // Affichage du message dans la fentre g.drawString("Bonjour tous ", 110, 110); } } // fin classe FentreBonjour

AWT est un paquetage de lAPI JAVA qui propose toutes les classes ncessaires la construction de linterface graphique. Dans le code prcdent, la directive dimportation permet un accs direct aux classes de ce paquetage, et doit normalement apparatre en tte. La classe Frame dnit une simple fentre graphique avec une bordure et une barre de titre qui peut tre afche lcran et dans laquelle on pourra crire, dessiner ou placer des composants graphiques. La fentre graphique que nous voulons afcher possde toutes les proprits dun objet Frame avec un message lintrieur. La classe FentreBonjour hrite donc de la classe Frame. Son constructeur excute dabord celui de sa super-classe pour donner un titre qui safchera sur la barre de la fentre graphique. Puis, il excute la mthode setSize pour xer les dimensions de la fentre. Lunit de mesure est le pixel, qui correspond un point lumineux de lcran. La taille relle de la fentre dpend de la distance entre deux points lumineux, et plus prcisment de sa rsolution, cest--dire du nombre de pixels par ligne et par colonne. Plus la rsolution est grande, plus la fentre apparatra petite, et plus la prcision sera grande. Dans notre exemple, la fentre est donc un rectangle de largeur 300 pixels et de hauteur 200 pixels. Enn, le constructeur excute la mthode setVisible avec comme paramtre true pour afcher la fentre lcran. Notez quune fentre graphique peut exister, sans pour autant tre visible sur lcran. Voil pourquoi lappel setVisible est ncessaire. La mthode paint nest appele nulle part dans le programme. Cest lenvironnement AWT qui sen charge automatiquement la cration de la fentre ou lors de sa dsicnication. Son paramtre de type Graphics est la zone graphique de la fentre dans laquelle la mthode drawString crira le message. Les coordonnes du point de dpart du message sont (110, 110). Le point (0, 0) est langle suprieur gauche. La cration dun objet de type FentreBonjour provoque la cration dune fentre semblable celle de la gure 25.5. Dans AWT, un vnement est un objet. Tous les composants graphiques peuvent mettre des vnements, et peuvent tre aussi lcoute des vnements grce des auditeurs (listeners en anglais). Lauditeur dun objet spcie les vnements quil dsire couter, et contient des fonctions de rappel excuter, appeles gestionnaires dvnements. Nous allons maintenant faire ragir cette fentre un vnement extrieur. Lorsquelle possde le focus, nous voulons que cette fentre se ferme et que lapplication sachve lorsque lutilisateur appuie sur la touche q de son clavier. La classe FermetureFentre qui suit fabrique par hritage de la classe abstraite KeyAdapter lauditeur charg de cette tche. La classe KeyAdapter traite tous les vnements issus des touches du clavier. Parmi les

374

Chapitre 25 Interfaces graphiques

F IG . 25.5 Une simple fentre.

mthodes abstraites de cette classe, nous allons dnir keyPressed dans lauditeur. Son paramtre de type keyEvent permet de tester la touche qui a t appuye grce la mthode getKeyChar(). Les classes qui grent les vnements appartiennent au paquetage java.awt.event.
import java.awt.event.*; public class FermetureFentre extends KeyAdapter { public void keyPressed(KeyEvent e) { if (e.getKeyChar() == q ) System.exit(0); } } // fin classe FermetureFentre

La mthode addKeyListener permet dajouter une fentre des auditeurs qui traitent la rception des vnements issus des touches du clavier. Pour ajouter lauditeur prcdent, il suft de placer dans le constructeur de la classe FentreBonjour linstruction suivante :
addKeyListener(new FermetureFentre());

An de contracter le code, notez que lauditeur peut tre galement dni par une classe anonyme dclare au moment de sa construction.
addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyChar() == q ) System.exit(0); }});

25.4.2

Convertisseur deuros

On se propose de programmer un convertisseur deuros en francs (et inversement). Le cahier des charges de ce convertisseur est simple : un utilisateur pourra saisir une somme dargent que le convertisseur convertira soit en francs soit en euros suivant le taux de conversion en vigueur. Il pourra aussi mettre n lapplication tout moment.

25.4

Interfaces graphiques en Java

375

F IG . 25.6 Un convertisseur deuros.

Avant de programmer linterface graphique de ce convertisseur, donne par la gure 25.6, nous allons dabord nous intresser son noyau fonctionnel. Les fonctions de conversion sont au cur de celui-ci. Ce noyau fonctionnel est reprsent par la classe Conversion suivante :
public class Conversion { // Le taux de conversion officiel : 1 euro = 6.55957 francs static final double TAUX_DE_CONVERSION = 6.55957; // Rle : convertir des francs en euros public static double convertirEnEuros(double francs) { return francs / TAUX_DE_CONVERSION; } // Rle : convertir des euros en francs public static double convertirEnFrancs(double euros) { return euros * TAUX_DE_CONVERSION; } } // fin classe Conversion

Pour crer linterface graphique, nous allons assembler des composants graphiques prdnis par la bote outils. Nous avons besoin dune entre pour la saisie de la somme dargent, et de trois boutons pour les conversions et lachvement de lapplication. Pour disposer ces composants graphiques lintrieur dune fentre graphique comme sur la gure 25.6, AWT propose diffrents systmes dagencement dnis par les classes FlowLayout, BorderLayout, GridLayout ou encore GridBagLayout. La mthode setLayout de la classe Container (dont hrite la classe Frame) permet de xer le type dagencement dsir. Ensuite, les appels successifs la mthode add permettent de placer les composants selon le mode dagencement choisi. La programmation en JAVA de linterface graphique du convertisseur est donne plus loin. Les quatre widgets de linterface sont dclars comme attributs de la classe EuroConvertisseur qui hrite de Frame. Lentre possde une largeur de 25 caractres au maximum, sans texte initial. Chacun des trois boutons est cr avec une tiquette qui lidentie. Pour lagencement de ces widgets, nous allons utiliser la classe GridLayout qui dispose les widgets sur une grille aux dimensions donnes, et la classe FlowLayout qui les dispose de faon rgulire de gauche droite et du haut vers le bas. La fentre principale est une grille deux lignes et une colonne. Lentre de type TextField est place sur la premire ligne. Les trois boutons sont placs dans la seconde ligne. Pour cela, ils sont regroups dans un objet de type Panel. Cette classe facilite la construction hirarchique des interfaces graphiques, puisque chaque panel pourra tre dcrit sparment et pourra possder son propre systme de coordonnes et son propre systme dagencement. La gure 25.7 montre lorga-

376

Chapitre 25 Interfaces graphiques

125.36
Frame (GridLayout(2,1))

Exit

Euros

Francs

Panel(FlowLayout)

F IG . 25.7 Agencement des composants graphiques.

nisation de linterface graphique avec ces deux systmes dagencement. Enn, la fentre est rendue visible, aprs que sa taille dnitive en fonction de celle de ses composants a t calcule grce la mthode pack.
import java.awt.*; public class EuroConvertisseur extends Frame { TextField monnaie = new TextField("", 25); Button euros = new Button("Euros"), francs = new Button(" F r a n c s "), exit = new Button(" E x i t "); public EuroConvertisseur() { // donner un titre la fentre super(" EuroConvertisseur "); // agencement principal : grille 2x1 setLayout(new GridLayout(2,1)); // positionner lentre avec un fond blanc monnaie.setBackground(Color.white); add(monnaie); // placer cte cte dans un panel les deux boutons // de conversion et celui de sortie Panel p = new Panel(); // dfinir son systme dagencement p.setLayout(new FlowLayout()); p.add(exit); p.add(euros); p.add(francs); // positionner le panel au-dessous de lentre add(p); // ajuster la taille des composants et // rendre visible la fentre pack(); setVisible(true); } } // fin classe EuroConvertisseur

Linterface prcdente ne ragit qu la seule insertion de caractres dans lentre. Cest le comportement par dfaut du widget TextField. Si lutilisateur appuie sur lun des

25.4

Interfaces graphiques en Java

377

boutons, son apparence change, et lvnement ActionEvent est gnr. La mthode actionPerformed est alors automatiquement excute dans lauditeur associ au bouton. Cet auditeur doit implmenter linterface ActionListener et dnir la mthode actionPerformed. Dans notre application, nous placerons cette mthode dans la classe EuroConvertisseur. laide, par exemple, de la mthode getSource de la classe ActionEvent, elle devra dterminer sur quel bouton lutilisateur a cliqu. Cette mthode renvoie une rfrence sur le bouton qui a t enfonc et il suft donc de tester sa valeur pour excuter lachvement de lapplication, ou pour remplacer la valeur relle contenue dans lentre par sa conversion en francs ou en euros.
public void actionPerformed(ActionEvent e) { if (e.getSource() == exit) System.exit(0); // sinon traiter la conversion double valeur=0; // prendre le contenu de lentre try { valeur = (Double.parseDouble(monnaie.getText())); } catch (NumberFormatException e) { monnaie.setText(" v a l e u r r e l l e errone"); return; } // valeur est bien un rel crire sa conversion dans lentre if (e.getSource() == euros) // bouton euros monnaie.setText( Double.toString(Conversion.convertirEnEuros(valeur))); else // bouton francs monnaie.setText( Double.toString(Conversion.convertirEnFrancs(valeur))); }

Lassociation dun auditeur un bouton se fait avec la mthode addActionListener. Dans notre exemple, et puisquelle possde la mthode actionPerformed, cest la classe EuroConvertisseur qui est elle-mme lauditeur des trois boutons5 . On applique alors la mthode addActionListener avec this en paramtre chacun des boutons.
exit.addActionListener(this); euros.addActionListener(this); francs.addActionListener(this);

25.4.3

Un composant graphique pour visualiser des couleurs

Dans ce dernier exemple, nous allons concevoir un composant graphique qui pourra tre utilis par ailleurs comme nimporte quel autre composant graphique dAWT6 . Avec ce composant, que nous appellerons CouleurGraphique, il sagit de crer, de modier et de visualiser des couleurs selon le modle RGB (Red, Green, Blue). Rappelons que dans ce modle, une couleur est dnie partir de la composition de trois valeurs, comprises
5 Notez que lon aurait pu associer un auditeur diffrent chacun des boutons. Lorsquil y a de nombreux boutons, cela permet dviter la cascade de tests pour dterminer le bouton sur lequel lutilisateur a appuy. 6 Le

paquetage Swing offre un tel composant, mais avec plus de fonctionnalits. Il se nomme JColorChooser.

378

Chapitre 25 Interfaces graphiques

un vert olive

F IG . 25.8 Composant de visualisation de couleurs.

entre 0 et 255, reprsentant une valeur dintensit des couleurs rouge, vert et bleu. Une couleur pourra tre construite et modie partir des constructeurs et des mthodes de la classe, ou bien de faon interactive laide de linterface graphique donne par la gure 25.8. Les barres de dlement font varier lintensit des trois couleurs dont les valeurs apparaissent dans les trois entres associes. Rciproquement, la modication des valeurs dintensit dans les entres ajustera la position courante des barres de dlement. Enn, quelle que soit la faon de modier les intensits, la nouvelle couleur spcie est visualise sur le haut de la fentre. Le widget CouleurGraphique est dni comme un Panel an quil puisse tre compos avec dautres widgets. Il est construit partir de dix autres composants graphiques : quatre tiquettes de type Label, trois barres de dlement de type Scrollbar et trois entres de type TextField. Ces widgets sont placs selon le systme dagencement BorderLayout qui organise le panel. Les trois tiquettes Rouge, Vert et Bleu sont places gauche (BorderLayout.EAST), les trois barres de dlement horizontales au centre (BorderLayout.CENTER), les trois entres droite (BorderLayout.WEST), et enn ltiquette qui visualise la couleur est positionne au-dessus (BorderLayout.NORTH). Les barres de dlement sont orientes de faon horizontale, gradues de 0 256, initialises 0 et la taille de leur curseur est dune unit. En fait, la valeur maximale atteinte sera 255 (256 - la taille du curseur). Les trois entres possdent galement une valeur initiale gale zro.
public class CouleurGraphique extends Panel { // les trois entres pour les intensits rouge, vert et bleu // initialises la valeur 0 TextField tfR = new TextField("0",3), tfV = new TextField("0",3), tfB = new TextField("0",3); // les trois barres de dfilement pour les intensits Scrollbar sbR = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 256), sbV = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 256), sbB = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 256); Label labRVB = new Label(), // pour visualiser la couleur choisie labR = new Label("Rouge"), labV = new Label(" Vert "), labB = new Label("Bleu");

25.4

Interfaces graphiques en Java

379

public CouleurGraphique () { setLayout(new BorderLayout()); // les tiquettes Rouge, Vert, Bleu gauche Panel p1 = new Panel(); p1.setLayout(new GridLayout(3,1)); p1.add(labR); p1.add(labV); p1.add(labB); add(p1,BorderLayout.WEST); // les barres de dfilement au centre Panel p2 = new Panel(); p2.setLayout(new GridLayout(3,1)); p2.add(sbR); p2.add(sbV); p2.add(sbB); add(p2, BorderLayout.CENTER); // les entres droite Panel p3 = new Panel(); p3.setLayout(new GridLayout(3,1)); p3.add(tfR); p3.add(tfV); p3.add(tfB); add(p3,BorderLayout.EAST); // ltiquette qui visualise la couleur au-dessus add(labRVB, BorderLayout.NORTH); } } // fin classe CouleurGraphique

Il sagit maintenant de relier ces composants graphiques une couleur. Pour cela, nous dclarons trois attributs entiers qui reprsenteront les trois intensits de la couleur courante. Ces attributs sont en fait des variables actives telles que leur modication entrane la mise jour des widgets auxquels ils sont relis ; rciproquement toute modication des widgets entrane un changement de valeur de ces attributs. Le code suivant est insr dans la classe CouleurGraphique. On dclare trois entiers rouge vert et bleu et on modie le constructeur de telle faon les initialiser laide des mthodes de changement de valeur dintensit (dcrites plus loin), qui provoquent galement la visualisation de la couleur dans ltiquette du haut.
int rouge, vert, bleu; public CouleurGraphique (int r, int v, int b) { ... add(labRVB, BorderLayout.NORTH); changerRouge(r); changerVert(v); changerBleu(b); } public CouleurGraphique () { this(0, 0, 0); }

Lorsquon modie une des intensits de la couleur courante, ce changement doit se rpercuter sur la barre de dlement et lentre qui lui correspondent, ainsi que sur ltiquette de visualisation du haut. Cest par exemple le rle de la mthode changerRouge donne au-dessous. Tout dabord, elle mmorise la nouvelle intensit rouge, puis laffecte la barre

380

Chapitre 25 Interfaces graphiques

de dlement (ce qui a pour effet de dplacer la position de son curseur) et lentre associe. Une nouvelle couleur courante est alors cre en conservant les intensits verte et bleue prcdentes, et on la visualise dans ltiquette.
public void changerRouge(int r) { // Rle : change la valeur de lintensit rouge et // rpercute la modification sur les widgets lis rouge = r; sbR.setValue(r); // ajuster la barre de dfilement tfR.setText(String.valueOf(r)); // puis lentre // visualiser la nouvelle couleur labRVB.setBackground(new Color(rouge, vert, bleu)); }

Les deux autres mthodes de modication des intensits verte et bleue scrivent sur le mme modle. Le dplacement du curseur des barres de dlement provoque lmission dvnements
AdjustmentEvent. La mthode adjustmentValueChanged est alors automatiquement

excute dans lauditeur associ la barre de dlement. Dans notre exemple, lauditeur est la classe CouleurGraphique. Elle implmente linterface AdjustmentListener et dnit la mthode adjustmentValueChanged. Cette dernire rpercute la modication de la barre de dlement laide des mthodes changerRouge, changerVert et changerBleu prcdentes.
public class CouleurGraphique extends Panel implements AdjustmentListener, ActionListener { ... public void adjustmentValueChanged(AdjustmentEvent e) { if (e.getSource() == sbR) changerRouge(sbR.getValue()); else if (e.getSource() == sbV) changerVert(sbV.getValue()); else // la barre de dfilement de lintensit bleue changerBleu(sbB.getValue()); } ... } // fin classe CouleurGraphique

Lappui sur la touche entre du clavier dans lune des entres associes aux intensits provoque lmission dvnements ActionEvent. La mthode actionPerformed de lauditeur associ est excute. Lauditeur doit alors implmenter linterface ActionListener. La classe CouleurGraphique est aussi lauditeur de ces vnements. Elle implmente donc ActionListener et dnit actionPerformed comme suit :
public void actionPerformed(ActionEvent e) { if (e.getSource() == tfR) changerRouge(Integer.parseInt(tfR.getText())); else if (e.getSource() == tfV)

25.4

Interfaces graphiques en Java

381

changerVert(Integer.parseInt(tfV.getText())); else // lentre de lintensit bleue changerBleu(Integer.parseInt(tfB.getText())); }

Pour terminer, il faut spcier les auditeurs des vnements mis par les barres de dlement et les entres. Puisque lauditeur qui contient les mthodes adjustmentValueChanged et actionPerformed est la classe CouleurGraphique, son association avec les widgets concerns se fait simplement dans le constructeur :
// associer lauditeur aux barres de dfilement sbR.addAdjustmentListener(this); sbV.addAdjustmentListener(this); sbB.addAdjustmentListener(this); // associer lauditeur aux entres tfR.addActionListener(this); tfV.addActionListener(this); tfB.addActionListener(this);

25.4.4

Applets

Les applets sont des petites applications graphiques JAVA intimement lies au World Wide Web, plus communment appel W EB7 . Contrairement aux applications graphiques prcdentes qui sexcutent de faon autonome, les applets ncessitent une autre application graphique pour leur excution, un visualisateur spcialis (appletviewer) ou un navigateur WWW. Leur contexte dexcution est celui dun document HTML interprt par le visualisateur charg de lexcution de lapplet. Le chargement de lapplet dans le document HTML se fait avec la balise OBJECT8 . Chaque applet a les moyens daccder des informations sur son contexte dexcution, en particulier sur ses paramtres de chargement ou sur les autres applets du document. Les composants graphiques habituels (boutons, labels, canevas, etc.) dAWT ou Swing peuvent tre utiliss pour la construction des applets, et ragissent normalement aux vnements (clics de souris, etc.). Les applets peuvent galement traiter des images (gnralement au format gif ) et des documents sonores (gnralement au format au), si lenvironnement dexcution dispose dun dispositif de reproduction sonore. Gnralement places sur des serveurs W EB, les applets sexcutent localement dans le navigateur de lutilisateur aprs leur tlchargement travers le rseau. Ce type de fonctionnement impose bien sr des rgles de scurit pour garantir lintgrit de la machine de
7 galement appel WWW, W3 ou encore la Toile. Le W EB est n au CERN en 1989 et, depuis lors, na cess de se dvelopper. Il est aujourdhui un des outils central du monde informatique. Rappelons, que lide fondamentale du W EB est de fournir la consultation, par lintermdiaire de serveurs spcialiss, des documents de type hypertexte, cest--dire contenant des rfrences, appeles liens, dautres documents, eux-mmes consults de la mme manire. Ces documents sont des chiers rdigs dans le langage de balise HTML (HyperText Markup Language). Ce langage est form dun ensemble de commandes disperses dans un texte ordinaire, qui permettent dune part de placer des indications de prsentation, dautre part de placer des documents graphiques ou sonores, ou encore des programmes excuter, enn de placer des rfrences dautres documents. Laccs au W EB se fait laide de navigateurs qui fournissent la plus part du temps une interface graphique, ncessaire pour les applets. 8 Depuis

la version 4.0 de HTML, la balise OBJECT remplace APPLET qui est devenue obsolte.

382

Chapitre 25 Interfaces graphiques

lutilisateur. Par exemple, une applet ne peut, par dfaut9 , lire ou crire des chiers ou excuter des programmes de la machine client. Laction dune applet est limite par un composant spcique du navigateur (SecurityManager) qui en assure le contrle. AppletEuroConvertisseur titre dexemple, nous allons reprendre le convertisseur deuros de la section 25.4.2 pour le transformer en applet. Lcriture dune applet avec AWT se fait ncessairement par hritage de la classe Applet. Len-tte de notre nouvelle classe AppletEuroConvertisseur, qui nhrite donc plus de la classe Frame, aura la forme suivante :
import java.applet.*; public class AppletEuroConvertisseur extends Applet implements ActionListener {

La classe Applet fournit une interface entre lapplet et son contexte dexcution par lintermdiaire dun certain nombre de mthodes. Plusieurs dentre elles peuvent tre rednies dans la classe hritire. En particulier les mthodes suivantes : init, appele par le visualisateur lors du premier chargement de lapplet ; start, appele par le visualisateur juste aprs init, ou automatiquement chaque rapparition lcran (changement de page ou dsicnication), pour signier cette applet quelle doit dmarrer ou redmarrer sa vritable excution ; stop, appele par le visualisateur pour arrter lexcution de lapplet chacune de ses disparitions de lcran (changement de page ou icnication) ou juste avant sa destruction complte ; destroy, appele par le visualisateur pour dtruire toutes les ressources alloues par lapplet. Pour notre applet, nous rednirons la mthode dinitialisation init. Celle-ci reprend les instructions du constructeur de la classe EuroConvertisseur qui agencent lentre et les boutons, et associent les auditeurs. Comme la classe Frame, Applet hrite de la classe Container et de sa mthode add pour ajouter les composants graphiques.
public void init() { setLayout(new GridLayout(2,1)); // lentre monnaie.setBackground(Color.white); add(monnaie); // les deux boutons de conversion Panel p = new Panel(); p.setLayout(new GridLayout(1,2)); p.add(euros); euros.addActionListener(this); p.add(francs); francs.addActionListener(this); add(p); }

Notez la disparition du bouton exit, qui na plus de raison dtre puisquune applet nest pas une application indpendante. La destruction de lapplet est gre par son visualisateur et
9 Elle

a la possibilit de le faire si elle possde une autorisation.

25.5

Exercices

383

la mthode destroy. De mme, les appels aux mthodes pack et setVisible deviennent inutiles puisque la gestion de lafchage est sous le contrle du visualisateur. Enn, le lancement de lexcution de lapplet est spci par la balise OBJECT place dans un document HTML qui sera interprt par le visualisateur. Cette commande indique le nom du chier compil et les dimensions dafchage de la fentre.
<OBJECT classid=" j a v a : AppletEuroConvertisseur . c l a s s " width=400 height=150> </OBJECT>

25.5

EXERCICES

Pour les exercices qui suivent, vous aurez besoin des pages de documentation des paquetages dAWT. Exercice 25.1. Modiez la classe EuroConvertisseur an de visualiser simultanment la valeur convertir et la valeur convertie. Exercice 25.2. Renommez DessinsGomtriques la classe FentreBonjour de la page 372, puis modiez sa mthode paint an dafcher dans la fentre des rectangles et des cercles. Utilisez les mthodes de la classe Graphics. Pour dessiner dans la zone graphique, on choisit dabord une couleur grce la mthode setColor. Ensuite, on fait appel lune des mthodes drawXXX qui dessinera la gure dsire avec la couleur xe. Exercice 25.3. Une barre de menus est un composant qui se place au-dessus dune fentre et qui contient plusieurs menus. Chaque menu apparat lcran lorsque lutilisateur clique dessus et propose plusieurs entres. chaque entre est associe une commande (ou ventuellement un sous-menu). Dans AWT, un composant MenuBar est une barre de menus qui contient des objets Menu contenant eux-mmes des objets MenuItem. Une barre de menus est ajoute avec la mthode setMenuBar. La mthode add de MenuBar permet lajout de menus de type Menu. Les entres dun menu, de type MenuItem, sont ajoutes laide de la mthode add de Menu. Enn, la mthode addActionListener de MenuItem permet dassocier un auditeur de type ActionListener une entre. Dans la classe DessinsGomtriques prcdente, placez au-dessus de la fentre une barre de menus qui contient les menus Gestion et Figures. Dans le premier menu, ajoutez lentre Quitter, et dans le second les entres Rectangle, Carr, Ellipse et Cercle. Puis, associez lentre Quitter laction dachvement de lapplication (i.e. exit). Exercice 25.4. La classe Canvas dAWT dnit une zone graphique dans laquelle il est possible de manipuler les dessins excuts. Cette classe possde la mthode paint, hrite de Component, qui est appele implicitement chaque fois quil est ncessaire de redessiner la zone graphique du canevas. Une classe hritire de la classe Canvas peut ds lors rednir la mthode paint et afcher toutes les informations voulues dans la zone graphique (le paramtre de type Graphics de paint). crivez une classe Toile hritire de Canvas. Ajoutez dans la fentre de la classe DessinsGomtriques de lexercice prcdent un objet de type Toile, puis associez chaque entre du menu Figures, la cration de la gure correspondante dans le canevas.

384

Chapitre 25 Interfaces graphiques

Aprs chaque cration, il faudra rafcher explicitement la zone graphique du canevas pour visualiser lcran la nouvelle gure. Pour cela, vous appellerez la mthode repaint (et non pas paint10 ). Vous utiliserez les classes dnies au chapitre 12 pour reprsenter les gures. Ajoutez dans la classe Figure un attribut qui dnit les coordonnes dorigine de la gure. Vous devrez galement mmoriser toutes les gures cres dans une table. La mthode paint de Toile parcourra cette table pour rafcher correctement toutes les gures. Pensez munir les gures dune mthode dessiner. Exercice 25.5. Lauditeur de type MouseListener intercepte les vnements de type bouton appuy ou relch, tandis que lauditeur de type MouseMotionListener traite les vnements lis aux mouvements du pointeur de la souris. Compltez la classe Toile avec les mthodes mousePressed et mouseDragged pour slectionner et dplacer une gure de la toile. Ces deux mthodes possdent comme paramtre un vnement de type MouseEvent partir duquel on rcupre les coordonnes du pointeur de souris avec les mthodes getX et getY. On considrera que la gure slectionne sera celle dont lorigine est la plus proche du pointeur de la souris. Pensez munir la classe Figure dune mthode distance qui renvoie la distance entre un point de la toile et le point dorigine de la gure courante, ainsi quune mthode dplacer qui change le point dorigine de la gure courante. Exercice 25.6. Modiez votre auditeur dvnements souris an de faire varier la taille des gures lorsquon tire sur une gure avec le deuxime bouton de la souris enfonc. La mthode getButton de MouseEvent indique le bouton enfonc. Pensez dnir dans vos classes de gures une mthode de dilatation selon un coefcient pass en paramtre. Exercice 25.7. laide dun objet CouleurGraphique (voir la page 377), compltez la classe DessinsGomtriques an de permettre le choix de la couleur des gures dessines. Si cela est ncessaire, la classe CouleurGraphique pourra tre tendue. Vous possdez maintenant lbauche dun diteur graphique et interactif de formes gomtriques que vous pouvez complter votre guise.

10 La mthode paint nest jamais appele directement. En fait, repaint appelle la mthode update qui appelle

paint.

Bibliographie

[ACM93] ACM SIGPLAN Notices. Conference on History of Programming Languages (HOPL-II), April 1993. [AFN82] [ANS76] [ANS78] [ANS83] [ANS89] [ASU89] [BBL95] AFNOR. Le Langage Pascal, spcication et mise en uvre. AFNOR, 2e dition, 1982. ANSI. American National Standard Programming Language PL/I, ANSI X3.53, 1976. ANSI. American National Standard Programming Language FORTRAN. ANSI, 1978. ANSI. The Programming language ADA, reference manual. Springer-Verlag, 1983. ANSI. Programming Language C, ANSI X3.159-1989. ANSI, 1989. A. Aho, R. Sethi et J. Ullman. Compilateurs, principes, techniques et outils. InterEditions, 1989. P. Brun et M. Beaudouin-Lafon. A taxonomy and evaluation of formalisms for the specication of interactive systems. Proc. Conference on Human-Computer Interaction, p. 197212, August 1995. C. Berge. Graphes et hypergraphes. Dunod, 1970.

[Ber70]
+

[BFL 92] L. Bass, R. Faneuf, R. Little, B. Pellegrino, S. Reed, S. Sheppard et M. Szczur. A Metamodel for the Runtime Architecture of an Interactive System . ACM SIGCHI Bulletin, 24, 1992. [BM93] [Bro99] Jon L. Bentley et M. Douglas McIlroy. Engineering a sort function . Software Practice and Experience, 23(11) :12491265, 1993. J. Brondeau. Introduction la programmation objet en Java. Dunod, 1999.

[CKvC83] A. Colmerauer, H. Kanaoui et M. van Caneghem. Prolog, bases thoriques et dveloppements actuels . TSI, 2 :271311, juillet-aot 1983.

386

Bibliographie

[Cob60] [Cou87]

COBOL : Initial Specications for a Common Business Oriented Language. Dept. Of Defense, U.S. Gov, Printing Ofce, 1960. J. Coutaz. Pac : An object oriented model for dialog design. H.-J. Bullinger et B. Shakel, diteurs, Human-Computer Interaction : INTERACT87, p. 431436. North-Holland, Amsterdam, 1987. O-J Dahl et K. Nygaard. Simula An Algol-based Simulation Language . Comm. ACM, 9, 1966. B. Eckel. Thinking in Java. Prentice Hall PTR, 2e dition, 2000. C. Froidevaux, M. C. Gaudel et M. Soria. Types de donnes et Algorithmes. Mc Graw Hill, 1990. David Flanagan. JavaScript : The Denitive Guide. OReilly, 5e dition, 2010. David Flanagan et Yukihiro Matsumoto. The Ruby Programming Language. OReilly, 1e dition, 2008. R. Griswold et M. Griswold. The Icon Programming Language. Peer-to-Peer Communications, 3e dition, 1996.

[DN66] [Eck00] [FGS90] [Fla10] [FM08] [GG96]

[GHK79] R. E. Griswold, D. R. Hanson et J. T. Korb. The Icon programming language an overview . ACM SIGPLAN Notices, 14 :1831, 1979. [Gia91] [GJS96] [Gon95] [GR89] [GR08] D. Giampaolo. Libsx v1.1 The Simple X library, 1991. J. Gosling, B. Joy et G. L. Steele. The Java language specication. AddisonWesley, 1996. M. Gondran. Graphes et algorithmes. Eyrolles, 3e dition, 1995. A. Goldberg et D. Robson. Smalltalk-80, The Language. Addison-Wesley, 2e dition, 1989. Vincent Granet et Jean-Pierre Regourd. Aide-Mmoire de Java. Dunod, 2e dition, 2008.

[HNR68] P. E. Hart, N. J. Nilsson et B. Raphael. A Formal Basis for the Heuristic Determination of Minimum Cost Paths . IEEE TSSC, 4, July 1968. [Hoa69] [Hoa72] [Hor83] [Int57] [Knu67] [Knu73] [Kor85] C. A. R. Hoare. An axiomatic basis for computer programming . Comm. ACM, Octobre 1969. C. A. R. Hoare. Notes on data structuring. Structured Programming. Academic Press, 1972. E. Horowitz, diteur. Programming Languages, A Grand Tour. Springer-Verlag, 1983. International Business Machines Corporation. FORTRAN : an automatic coding system for the IBM 704. IBM Corporation, New York, NY, USA, 1957. D. E. Knuth. The Remining Troublespots in Algol 60 . Comm. ACM, 10, 1967. D. E. Knuth. Sorting and Searching. The Art of Computer Programming, vol. 3. Addison Wesley, 1973. R. E. Korf. Depth-First Iterative-Deepening : An Optimal Admissible Tree Search . Articial Intelligence, 27 :97109, September 1985.

Bibliographie

387

[KP88] [KR88] [L+ 77] [LC06] [Lut09] [M. 81] [M+ 89] [Mac10] [MB86] [Mey88] [Mey92] [Mey97]

G. Krasner et S. Pope. A Cookbook for Using the Model-View-Controller User Interface Paradigm in Smalltalk -80 . JOOP, 1 :2649, August/Spetember 1988. B. W. Kernighan et D. M. Ritchie. The C Programming Language. Prentice Hall, 2e dition, 1988. B. Liskov et al. Abstraction Mecanisms in Clu . Comm. ACM, 20 :564576, june 1977. Gary T. Leavens et Yoonsik Cheon. Design by contract with jml, 2006. Mark Lutz. Learning Python, Fourth Edition. OReilly, 2009. M. Shaw and others. Alphard : Form and Content. Springer-Verlag, 1981. G. Masini et al. Les langages objets. InterEdition, 1989. Peter MacIntyre. PHP : The Good Parts. OReilly, 1e dition, 2010. M. Minoux et G. Bartnik. Graphes algorithmes logiciels. Dunod, 1986. B. Meyer. Conception et programmation par objets. InterEditions, 1988. B. Meyer. Eiffel : The Language. Object-Oriented Series. Prentice Hall, 1992. B. Meyer. Object-oriented software construction. Prentice Hall, 2e dition, 1997.

[NAJN75] K. V. Nori, V. Ammann, K. Jensen et Ngeli. The Pascal (P) compiler : Implementation Notes. Institut fr Informatik, Eidgenssische Technische Hochschule, 1975. [Nau60] [OSV08] [Ous94] [Pfa85] [Sam69] [Sch90] [Sed04] [SG08] [Ska00] [Str86] [Tan06] [W+ 76] [Wex81] P. Naur. Report on the Algorithmic Language Algol 60 . Comm. ACM, 3, 1960. Martin Odersky, Lex Spoon, et Bill Venners. Programming in Scala. Artima Inc., 2008. John K. Ousterhout. Tcl and the Tk Toolkit. Addison Wesley, 1994. G. Pfaff. User interface management systems. Proceedings of the Seeheim Workshop. Springer-Verlag, 1985. J. E. Sammet. Programming Languages, History and Fundamentals. Series in Automatic Computation. Prentice-Hall, 1969. D. C. Schmidt. Gperf : A perfect hash function generator. Second USENIX C++ Conference Proceedings, April 1990. Robert Sedgewick. Algorithmes en C++. Pearson Education, 3e dition, 2004. Andrew Stellman et Jennifer Greene. C#. Digit Books, 2008. J. Skansholm. Java From the Beginning. Addison Wesley, 2000. B. Stroustrup. The C++ Programming Language. Addison-Wesley, 2e dition, 1986. Andrew Tanenbaum. Architecture de lordinateur. Pearson Education, 5e dition, 2006. W. A. Wulf et al. An introduction to the construction and verication of Alphard programs . IEEE Transactions on Software engineering, 2(3) :253264, 1976. R. L. Wexelblat, diteur. History of Programming Languages. Academic Press, 1981.

388

Bibliographie

[Wir75] [Wir76] [Wir85]

N. Wirth. Introduction la programmation systmatique. Masson, 1975. N. Wirth. Algorithms + Data Structures = Programs. Printice-Hall, 1976. N. Wirth. Programming in MODULA-2. Springer-Verlag, 2e dition, 1985.

Index

A
A*, 332 abstraction, 175 accs aux attributs, 67 aux mthodes, 70 achvement (prdicat d), 81 ACKERMANN W., 168 action lmentaire, 15 structure, 43 adressage dispers, 279286 affectation, 18 A L -K HOWRIZM, 11 A LGOL 60, 8, 9 A LGOL 68, 9, 115 algorithme, 11 algorithme de B OYERM OORE, 107109, 112 D IJKSTRA, 327332 E UCLIDE, 86 F LOYD, 340 F LOYDWARSHALL, 325 WARSHALL, 340 allocation mmoire, 139, 171 A LPHARD, 9, 13, 178 analyse, 6, 111 antcdent, 12 applets, 381

arbre, 225232 2-3-4, 264269 AVL, 256264 bicolore, 269279 binaire, 232240, 290 complet, 233 couvrant, 323 de jeu, 350 quilibr, 252, 256 ordonn, 252 parfait, 233, 290 quaternaire, 288 Arbre, 227229 Arbre b , 234235 arcs, 211, 226 artes, 211 A DA, 9, 179 A SCII, 29, 362 assert, 13, 40 attribut, 65 partag, 68 automate, 1 AVL, voir arbre AVL AWT, 371, 372 axiomatique de C.A.R. H OARE, 12 description, 176

B
backtracking, voir rtro-parcours

390

Algorithmique et programmation en Java

BACKUS J., 8, 9 barre de titre, 367 bicolore, voir arbre bicolore botes outils, 370 boolean, 29 boucle, voir nonc itratif B OYER, voir algorithme de M OORE byte, 24

B OYER

C
C, XVI, 5, 9, 10, 22, 38, 40, 51, 92, 93, 97, 102, 171 C++, 10, 40, 179 C#, 10 callbacks, voir fonctions de rappel carr magique, 120 cast, voir conversions de type catch, 141 CEI, 30 chanage externe, 284 chanes de caractres, 97 char, 30 chemin dans un arbre, 226 dans un graphe, 213 eulrien, 341 C HU S HIH-C HIEH, 104 C HURCH A., 8, 161 class, 68 classe, 6579 abstraite, 133 anonyme, 374 hritire, 127 Cl , 244 e cl, 243, 299 client-serveur, 366 clients, 65 C LU, 9 C OBOL, 8, 9 codage, 5, 241 Collection, 199 collision, 280 C OLMERAUER A., 10 commande, voir unit de commentaires, 13, 22 compilateur, 6 complment deux, 24 un, 24

complexit, 12, 110 composante connexe, 323 composants logiciels, 127 compression de chier, 241 concatnation, 97 confrontation de modle, 10, 106 connexit, 325 consquent, 12 Console, 159 constante, 18 (complexit), 111 constructeur, 66 conversions de type explicites, 38 implicites, 38 couleur, 368, 369, 377 coupure , 353 crible d RATOSTHNE, 98, 159

D
D E M ORGAN, 28 dclaration dun nom, 18 dcor, 367 degr, 213 D`que, 205206 e dque, 183, 205207 descendant, 128 description axiomatique, 176 fonctionnelle, 176 dictionnaire, voir table D IJKSTRA, voir Algorithme de do, 83 documentation, 22, 52 donne, 2, 15 double, 27 D RER A., 120

E
E BCDIC, 29 criture, 17, 147 effet de bord, 58 E IFFEL, 10, 13, 70, 140, 178, 179 en-tte, 52 nonc choix, 44 compos, 43 conditionnel, 44 itratif, 81

Index

391

rpter, 83 si, 46 tantque, 82 entre standard, 15 numration, 196 environnement logiciel, 1 matriel, 1 quipements externes, 2 RATOSTHNE, 98, 99, 159 E UCLIDE, 86, 89 E ULER L., 341 valuation, 36 vnements, 366, 371 exception, 139 excution parallle, 43 squentielle, 43 expressions, 35 extends, 135

fournisseur, 65 frres, 226 FSF, 5 fusion, 151, 317

G
garbage-collector, 212 G AUSS C. F., 123, 124, 345 gnrateur alatoire, 151 gnricit, 136 gomtrie, 367 gestionnaire de fentres, 4, 366 grands entiers, 111 graphe, 211223 connexe, 213 dhritage, 134 non orient, 211 orient, 211 sans cycle, 338 simple, 212 valu, 212, 214, 216, 219 Graphe, 213214 G RISWOLD R., 10

F
fentre, voir systme de fentrage fermeture transitive, 325 feuilles, 226 F IBONACCI, 163 chier, 145 de texte, 155 squentiel, 145, 317 FIFO, voir le le, 183, 203205 File, 203204 FilePriorit , 289290 e ls, 226 final, 21 nitude, 84 float, 27 ot, 149 F LOYD, voir algorithme de ux, voir ot focus, 367 fonction, 16, 51 fonctionnelle (description), 176 fonctions dadressage, 279 de rappel, 371 secondaires, 282 for, 102 For t, 227229 e F ORTRAN, 8, 9, 115

H
hash-coding, voir adressage dispers heapsort, voir tri en tas hritage, 127, 128 multiple, 134 simple, 134 H RON LA NCIEN, 89 H ILBERT (courbes de ), 169 H OARE C.A.R, 12, 175, 312, 315 H ORNER W. (schma de ), 103, 104, 112 HTML, 22, 381, 383 H UFFMAN (code de ), 241 huit reines, 345

I
IBM, 8, 29 I CON, 10, 36 IDA*, 334 identicateur de constante, 18 de fonction, 52 de procdure, 52 de variables, 19 IEEE, 26, 27 if, 47

392

Algorithmique et programmation en Java

implantation, 178 dArbre, 229 dArbre b , 235 de D`que, 206 e de File, 204 de For t, 230 e de Graphe, 214 de Liste, 185 de Pile, 200 de Table, 246 numration, 197 import, 137 indexation, 91 indice, 91 instances, 66 int, 24 interclassement, 317 interface, 136 graphique, 365383 textuelle, 361 utilisateur, 361, 363 interpolation, 286 interprte, 7 de commandes, 4 invariant de boucle, 81 de classe, 72, 140 ISO, 29, 30 ISO-8859, 29 Iterator, 197 iterator, 199

dassemblage, 5 de programmation, 4 intermdiaire, 7 machine, 4 non typ, 23, 131 typ, 23, 131 lecture, 15, 148 lexical, 6 liaison dynamique, 133 LIFO, voir pile linaire (complexit), 111 lisibilit, 52 L ISP, 810, 35 L ISP 2, 8 liste, 183196 dadjacence, 215, 218, 231 non ordonne, 246 ordonne, 248 Liste, 184185 localisation, 52 logarithmique (complexit), 111 logiciel, 4 libre, 5 long, 24 UKASIEWICW JAN, 35

M
main, 21, 72

J
JAVA, 10
javadoc, 22

JAVA S CRIPT, 10
JColorChooser, 377

jeux de stratgie, 350 JVM (Java Virtual Machine), 7, 10

K
K NUTH D., 9, 310, 321 KORF R. E., 334 K RUSKAL, 325

L
labyrinthe, 343, 358 langage objets, 10

M ANHATTAN (Distance de ), 333, 340 matrice, 115 creuse, 209 dadjacence, 215 produit, 119 symtrique, 118 M C C ARTHY J., 8 mdiane, 315, 321 mmoire caches, 2 centrale, 1 principale, 1 rcuprateur de , 212 secondaires, 3 menus, 367 mthode, 65, 69 abstraite, 133 de G AUSS, 123 de S IMPSON, 113 des rectangles, 112 rednition, 130 surcharge, 70

Index

393

M EYER B., 65, 70, 72, 135 MinMax, 350 modle ARCH, 365 de S EEHEIM, 364 MVC, 364, 372 M ODULA, 9 monotonies, 317 M OORE, voir algorithme de B OYER M OORE M ORGENSTERN O., 350 multi-paradigme, 10 multigraphe, 212

N
new, 68

Nud , 227 nuds, 226 notation O, 110 polonaise, 35 postxe, 35 prxe, 35 noyau fonctionnel, 363 null, 94

O
objets, 15 objets dynamiques, 65 occurrence, 106 octet, 2, 149 oprandes, 35 oprateurs, 35 oprations abstraites, 175 ordinateur, 1

P
pre, 226 paramtrage, 52 paramtre, 52 effectif, 16, 54 formel, 52 parcours darbre, 231 darbre binaire, 237 de graphe, 219223 de liste, 196 partition, voir tri rapide PASCAL, 9, 18, 38, 51, 92, 97, 115, 155, 171

pattern matching, voir confrontation de modle P EANO G., 177 priphriques, 2 permutation, 173, 299 pgcd, 86 PHP, 10 pile, 183, 199203 Pile, 199200 pivot, 312 pixels, 367 PL/I, 9 plus court chemin, voir algorithme de D IJKSTRA pointeurs, 171 polymorphisme, 132 polynme, 103, 208 portabilit, 7 post-condition, 12 pr-condition, 12 preuve, 175 P RIM, 325 PrintWriter, 159 priorit, 36 private, 68 procdure, 16, 51 produit de matrices, 119 programmation contractuelle, 73 descendante, 11 objet, 10 programme, 4 P ROLOG, 10 promotions, 38 prototype, 52 public, 68 puits (dun graphe), 223 P YTHON, 10

Q
quadratique (complexit), 111 quicksort, voir tri rapide

R
rcuprateur de mmoire, 212 Reader, 159 reader, 159 recherche, 243 auto-adaptative, 248

394

Algorithmique et programmation en Java

de chanes, 106 dichotomique, 250251 en arbre, 252 linaire, 246, 249 par interpolation, 286 rcursivit, 161 des actions, 162 des objets, 170 rednition, voir mthodes rfrence, 66 registres, 2 rgles de dduction, 13 de priorit, 36 rsultat, 15 rtro-parcours, 343350 return, 72 rutilisabilit, 11, 127 RHO, 333 R ITCHIE D., 9 rotations, 256259 RUBY, 10

structure de donnes, 175 structures linaires, 183 succ, 196 successeur, 183 super, 136 super-classe, 136 surcharge, voir mthode Swing, 372 switch, 45 syntaxique, 6 systme dexploitation, 4 de fentrage, 362 interactif, 365

T
table, 243 dadressage dispers, 279 de vrit, 28 Table, 244 tableau, 9196 plusieurs dimensions, 115122 circulaire, 188 TANENBAUM A., 3 tas, 290297, 302, 332 temps partag, 4 this, 68 this(), 72 throw, 142 throws, 142 toString, 75 traducteur, voir compilateur traitement par lots, 4 transmission par rsultat, 55 par valeur, 55 tri, 299320 bulles, 311 en tas, 302, 305 externe, 299, 317320 fusion, 320 fusion naturelle, 317 idiot, 320 insertion squentielle, 306 interne, 299 rapide, 312315 slection directe, 301 SHELL, 308310 stable, 320 topologique, 335339

S
S CALA, 10
Scanner, 159

S EDGEWICK R., 321 smantique, 6 sentinelle, 247 squence, 183 S HELL D.L., 308 short, 24 signature, 52 S IMPSON, 113 S IMULA, 10 SL5, 10 S MALLTALK, 10, 364 S NOBOL, 10, 97 sommets, 211 sortie standard, 17 sous-arbres, 226 sous-classes, 135 sous-programme, 51 stable, voir tri stable S TALLMAN R., 5 static, 68, 72 S TRAHLER (nombre de ), 240 S TRASSEN, 120 stream, voir ot structuration, 52

Index

395

topologique inverse, 338


try, 141

type, 23 abstrait algbrique, 175 boolen, 28 caractre, 29 lmentaire, 23 entier, 24 numr, 31 chier, 145 intervalle, 32 rel, 25 simple, 23 structur, 65

de commande, 2 UTF16, 30 UTF32, 30 UTF8, 30

V
variable, 17 VON N EUMANN J., 1, 350

W
WARSHALL, voir algorithme de W EB, XV, XVII, 4, 10, 381 while, 83 widget, 370 W IRTH N., 9, 12 writer, 159

U
U NICODE, 2931, 50, 97, 98, 108, 156, 159 unit arithmtique et logique, 2 centrale de traitement, 2 dchanges, 2

X
X EROX, 361

Вам также может понравиться