Poly

906 views

Published on

Published in: Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
906
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
21
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Poly

  1. 1. Programmation et Algorithmique ´ Ecole Polytechnique ´ Jean Berstel et Jean-Eric Pin
  2. 2. Avant-propos Ce polycopi´ est utilis´ pour le cours INF 421 intitul´ Les bases de la programmation et de e e el’algorithmique. Ce cours fait suite au cours INF 311 Introduction a l’informatique et pr´c`de le ` e ecours INF 431 intitul´ Fondements de l’informatique. e Ce polycopi´ reprend et d´veloppe les chapitres 10 et 11 du polycopi´ de Robert Cori, e e eJean-Jacques L´vy et Fran¸ois Morain Les bases de la programmation et de l’algorithmique. e cIl fait ´galement suite au polycopi´ de Fran¸ois Morain Les bases de l’informatique et de la e e cprogrammation. Pour qu’il n’y ait pas confusion avec ce dernier polycopi´, nous avons intitul´ e ele nˆtre plus simplement Programmation et algorithmique. o Comme le lecteur pourra le constater, nous avons suivi pour l’essentiel le canevas des cha-pitres 10 et 11 du polycopi´ de Robert Cori, Jean-Jacques L´vy et Fran¸ois Morain, en incluant e e cdes applications et des d´veloppements sur certaines variantes. Nous avons aussi insist´, au e ed´but, sur le concept de r´f´rence, notion qui se retrouve d’ailleurs, sous le terme de pointeur, e eedans d’autres langages de programmation. Le th`me principal du cours est, du cˆt´ de la programmation, la conception et la mise e oeen œuvre de nouveaux types. Le langage Java le permet de deux fa¸ons, par la cr´ation de c etableaux, et par la d´finition de classes. Quant a l’algorithmique, c’est la description et l’emploi e `de structures de donn´es dynamiques qui sont au centre de ce cours. Nous traitons en d´tail e eles listes et les arbres. Leur usage dans la repr´sentation de s´quences et d’ensembles ordonn´s e e eest particuli`rement d´velopp´. Les applications concernent la gestion des partitions (« union- e e efind » en anglais), la compression de textes, les t´trarbres (« quadtrees ») et des probl`mes e eg´om´triques qui ne sont pas tous d´crits dans cette premi`re version du polycopi´. Les structures e e e e eplus ´labor´es, comme les graphes, seront ´tudi´es dans le cours 431. e e e e Nous tenons a remercier en tout premier lieu Robert Cori, Jean-Jacques L´vy et Fran¸ois ` e cMorain qui nous ont autoris´ a reprendre a notre compte certains ´l´ments de leurs polycopi´s. e` ` ee e e ´Nous remercions aussi nos coll`gues de l’ Ecole Polytechnique, et plus particuli`rement Philippe e ´Chassignet, Emmanuel Coquery, Fabrice Le Fessant, Laurent Mauborgne, Eric Schost, AlexandreSedoglavic et Nicolas Sendrier qui ont largement contribu´ a l’´laboration et a la mise au point e` e `de ce cours. Enfin nous sommes particuli`rement reconnaissants a Catherine Bensoussan et au e ` ´service de reprographie de l’Ecole Polytechnique pour leur travail exemplaire. Les auteurs peuvent ˆtre contact´s par courrier ´lectronique aux adresses suivantes : e e e berstel@univ-mlv.fr Jean-Eric.Pin@liafa.jussieu.frOn peut aussi consulter les pages Web des auteurs : http : //www-igm.univ-mlv.fr/~berstel http : //liafa.jussieu.fr/~jep 3
  3. 3. Table des mati`res eI Compl´ments de programmation e 9 1 R´f´rences . . . . . . . . . . . . . ee . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.1 Types et r´f´rences . . . . ee . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.2 R´f´rences d’objets . . . . ee . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.3 Constructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2 M´thodes et variables statiques . e . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.1 Variables statiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.2 M´thodes statiques . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3 M´thodes et variables final . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 4 La classe String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21II Structures s´quentielles e 25 1 Listes chaˆ ees . . . . . . . . . . . . . . . . . ın´ . . . . . . . . . . . . . . . . . . . . . 26 1.1 D´finitions . . . . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . 26 1.2 Fonctions simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 1.3 Op´rations . . . . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . 30 Copie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Recherche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Suppression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Concat´nation . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . 33 Inversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 1.4 Une application : les polynˆmes . . . o . . . . . . . . . . . . . . . . . . . . . 36 2 Listes circulaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 2.1 D´finitions . . . . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . 41 2.2 Op´rations . . . . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . 43 Parcours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 2.3 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Permutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3 Variations sur les listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4 Hachage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.1 Adressage direct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 4.2 Tables de hachage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 4.3 R´solution des collisions par chaˆ e ınage . . . . . . . . . . . . . . . . . . . . . 49 4.4 Adressage ouvert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.5 Choix des fonctions de hachage . . . . . . . . . . . . . . . . . . . . . . . . . 52 5
  4. 4. 6 ` TABLE DES MATIERESIII Piles et files 55 1 Piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 1.1 Implantation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 2 Les exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 2.1 Syntaxe des exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 2.2 Lev´e d’exceptions . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . . 62 2.3 Capture des exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 3 Une application : l’´valuation d’expressions e arithm´tiques . e . . . . . . . . . . . . . 64 4 Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.1 Implantation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Implantation par tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Implantation par listes chaˆ ees . . ın´ . . . . . . . . . . . . . . . . . . . . . . 69 Choix de l’implantation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70IV Arbres 73 1 D´finitions . . . . . . . . . . . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 73 1.1 Graphes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 1.2 Arbres libres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 1.3 Arbres enracin´s . . . . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 74 1.4 Arbres ordonn´s . . . . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 75 2 Union-Find, ou gestion des partitions . . . . . . . . . . . . . . . . . . . . . . . . . 75 2.1 Une solution du probl`me . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 75 2.2 Applications de l’algorithme Union-Find . . . . . . . . . . . . . . . . . . . 79 3 Arbres binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 3.1 Compter les arbres binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 3.2 Arbres binaires et mots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 Mots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Ordres sur les mots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Codage des arbres binaires . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.3 Parcours d’arbre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.4 Une borne inf´rieure pour les tris par comparaisons e . . . . . . . . . . . . . 83 4 Files de priorit´ . . . . . . . . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 84 4.1 Tas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 4.2 Implantation d’un tas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 4.3 Arbres de s´lection . . . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 89 5 Codage de Huffman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 5.1 Compression des donn´es . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 90 5.2 Algorithme de Huffman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Codes pr´fixes et arbres . . . . . . . . . . . . . . . e . . . . . . . . . . . . . 91 Construction de l’arbre . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Choix de la repr´sentation des donn´es . . . . . . . e e . . . . . . . . . . . . . 94 Implantation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 5.3 Algorithme de Huffman adaptatif . . . . . . . . . . . . . . . . . . . . . . . 97V Arbres binaires 101 1 Implantation des arbres binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 1.1 Implantation des arbres ordonn´s par arbres binaires e . . . . . . . . . . . . 104 2 Arbres binaires de recherche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 2.1 Recherche d’une cl´ . . . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . 106 2.2 Insertion d’une cl´ . . . . . . . . . . . . . . . . . . . . e . . . . . . . . . . . . 107
  5. 5. `TABLE DES MATIERES 7 2.3 Suppression d’une cl´ . . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . 108 2.4 Hauteur moyenne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 3 Arbres ´quilibr´s . . . . . . . . . . . . . . . . . . . e e . . . . . . . . . . . . . . . . . 112 3.1 Arbres AVL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 Rotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 Implantation des rotations . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 Insertion et suppression dans un arbre AVL . . . . . . . . . . . . . . . . . 115 Implantation : la classe Avl . . . . . . . . . . . . . . . . . . . . . . . . . . 116 3.2 B-arbres et arbres a-b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 Arbres a-b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Insertion dans un arbre a-b . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Suppression dans un arbre a-b . . . . . . . . . . . . . . . . . . . . . . . . . 120VI Applications 123 1 Recherche dans un nuage de points . . . . . . . . . . . . . . . . . . . . . . . . . . 123 1.1 Construction de l’arbre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 1.2 Recherche de points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 2 T´trarbres . . . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . 126 3 Le probl`me des N corps . . . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . 128 3.1 Donn´es . . . . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . . . . . . 129 3.2 Vecteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 3.3 Corps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 3.4 Arbre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 3.5 Calcul des forces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 3.6 Univers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135Bibliographie 136Index 137
  6. 6. Chapitre ICompl´ments de programmation e Ce premier chapitre nous permettra de pr´ciser certains points cruciaux de la programmation een Java. Une premi`re section est consacr´e a l’´tude d´taill´e des r´f´rences, ou adresses. La e e ` e e e eeseconde section fait le point sur les m´thodes et les variables statiques. Nous terminons par equelques rappels sur les m´thodes et les variables d´clar´s final. e e e1 R´f´rences ee L’utilisation des adresses se retrouve dans divers langages de programmation (Pascal, C,C++, Java, etc.) et peut paraˆ ıtre un peu d´routante au d´but. Elle devient limpide d`s que e e el’on en a assimil´ les principes de base et c’est la raison pour laquelle nous y consacrons cette esection.1.1 Types et r´f´rences ee En Java, les donn´es d’un programme se r´partissent en deux cat´gories selon leur type : e e eles types primitifs et les autres. Les types primitifs comprennent les types d’entiers (byte, short,int, long), les types des r´els (float, double), le type des caract`res (char) et le type des bool´ens e e e(boolean). Les autres types sont les types de tableaux ou d’objets. Par exemple, String est un typed’objets, et int[] est le type d’un tableau d’entiers. La construction de tableaux et la d´finition ede nouvelles classes sont les deux moyens de cr´er de nouveaux types a partir de types donn´s. e ` eAinsi, une d´finition comme e class Personne { String nom; int age; }d´finit un nouveau type d’objets. Ces deux m´canismes s’emboˆ e e ıtent. Ainsi, class Annuaire { Personne[] liste; }d´finit un type d’objets ayant pour membres un tableau d’objets de type Personne. e La manipulation des donn´es dans un programme Java d´pend de leur type. La r´f´rence e e eed’une donn´e est l’adresse m´moire o` est log´e la donn´e. La nature pr´cise de l’adresse (entier, e e u e e emani`re de num´roter, etc) nous int´resse peu. Une r´f´rence est de plus typ´e par le type de la e e e ee edonn´e. e 9
  7. 7. 10 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATIONR`gle. Les donn´es de type primitif sont manipul´es par valeur, les donn´es des autres types e e e esont manipul´es par r´f´rence. e ee Ainsi, le contenu d’une variable de type int est un entier, le contenu d’une variable de typechar est un caract`re, et plus g´n´ralement le contenu d’une variable de type primitif est une e e evaleur de ce type. En revanche, le contenu d’une variable de type non primitif est l’adresse d’unedonn´e de ce type. La manipulation de r´f´rences est bien plus limit´e que la manipulation de e ee etypes de base : on peut seulement les comparer (par == et !=) et les affecter. L’affectation estune affectation de r´f´rences, et il n’y a donc pas de copie des donn´es r´f´renc´es ! On peut ee e ee eillustrer ce ph´nom`ne par un exemple concret. Si un ami vous annonce son d´m´nagement et e e e evous donne ses nouvelles coordonn´es, une simple modification dans votre carnet d’adresses vous epermettra de continuer a lui ´crire ou a lui t´l´phoner. Bien entendu, cette modification n’a pas ` e ` eeeu pour effet le d´m´nagement ou le changement de la ligne t´l´phonique de votre ami. C’est e e eela mˆme chose en Java. Lorsqu’on modifie une r´f´rence, cela n’entraˆ pas le d´placement e ee ıne ephysique des donn´es, qu’il faudra donc r´aliser ind´pendamment si on le souhaite. e e e Voici un premier exemple. public static void main(String[] args) { byte[] t = {5, 2, 6}; System.out.println("Tableau " + t) ; }Le r´sultat est e > Tableau [B@f12c4eLa deuxi`me chaˆ s’interpr`te comme suit : les deux premiers caract`res repr´sentent le type e ıne e e ed’un tableau d’octets (le crochet ouvrant [ signifie « tableau » et B signifie « octet’)’, le symbole’@’ signifie « adresse », la fin est l’adresse en hexad´cimal. eRemarque. (Cette remarque peut ˆtre ignor´e en premi`re lecture) Lors d’une impression, la m´thode e e e eprintln cherche une repr´sentation des donn´es sous la forme d’une chaˆ de caract`res. Celle-ci est fournie e e ıne epar la m´thode toString qui est d´finie pour tout objet. Par d´faut, cette m´thode retourne une chaˆ de e e e e ınecaract`res form´e du nom de la classe, de ’@’ et de l’´criture hexad´cimale du code de hachage de l’objet. e e e ePar d´faut, le code de hachage d’un objet est obtenu en hachant l’adresse de l’objet. Dans notre exemple, ef12c4e est l’´criture hexad´cimale du code de hachage de l’adresse du tableau t. Fr´quemment, une classe e e ered´finit la m´thode toString. Par exemple, la classe String d´finit cette m´thode pour qu’elle retourne la e e e echaˆ de caract`res contenue dans la donn´e. Ainsi, la chaˆ ”Tableau ” est effectivement affich´e telle ıne e e ıne equelle. Une figure bien choisie aide beaucoup a la compr´hension du comportement des r´f´rences. ` e eePour ce faire, la valeur associ´e a une variable n est dessin´e dans une boˆ ´tiquet´e par n. e ` e ıte e eDans la figure 1.1, on repr´sente une variable enti`re n dont la valeur est 2. Quand la valeur e ed’une variable est une r´f´rence, c’est-`-dire l’adresse d’une donn´e, la valeur num´rique de cette ee a e eadresse est remplac´e par une fl`che dirig´e vers l’emplacement de la donn´e. e e e e n 2 Fig. 1.1 – Une variable n contenant la valeur 2. Dans la figure 1.2, on repr´sente une variable de tableau d’octets t. La valeur de cette variable eest le d´but de la zone m´moire r´serv´e au tableau d’octets. e e e e
  8. 8. ´ ´1. REFERENCES 11 t @f12c4e @f12c4e 5 2 6 t 5 2 6 (a) (b)Fig. 1.2 – Une variable t contenant l’adresse d’un tableau de trois octets : (a) situation effective,(b) description symbolique. Quand une variable vaut null, cette valeur est indiqu´e par une petite croix, comme dans la efigure 1.3. t Fig. 1.3 – Une variable t contenant la valeur null. Comme nous l’avons dit, les op´rations sur les adresses sont restreintes. Voici un exemple ed’affectation. public static void main(String[] args) { byte[] t = {5, 2, 6}; byte[] s; s = t; System.out.println("Tableau " + t + " " + s) ; }Le r´sultat est e > Tableau [B@f12c4e [B@f12c4eAvant l’affectation s = t, la valeur de s est ind´finie. Apr`s l’affectation, les variables d´signent e e ele mˆme emplacement dans la m´moire (voir figure 1.4). e e t 5 2 6 t 5 2 6 s   s (a) (b) Fig. 1.4 – (a) avant affectation ; (b) apr`s affectation. e En effet, l’instruction d’affectation s = t copie le contenu de t dans s comme toute affectation,et le contenu est l’adresse du tableau. Continuons notre exploration. En toute logique, puisque apr`s l’affectation s = t, les variables es et t d´signent le mˆme emplacement m´moire, toute modification peut ˆtre faite indistinctement e e e evia s ou t. En particulier, une modification via s se rep`re aussi via t. Le programme e public static void main(String[] args)
  9. 9. 12 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATION { byte[] t = {5, 2, 6}; byte[] s = t; s[1] = 8; System.out.println(t[1]) ; }affiche en effet le nombre 8. A contrario, si l’on recopie les valeurs contenues dans le tableau t dans un autre tableau, lesdonn´es sont s´par´es. e e e public static void main(String[] args) { byte[] t = {5, 2, 6}; byte[] s = new byte[3]; for (int i = 0; i < t.length; i++) s[i] = t[i]; System.out.println(s + " " + t) ; System.out.println(s == t) ; }donne > [B@f12c4e [B@93dee9 > falseEn effet, avant la boucle for, le tableau s ne contient que des valeurs nulles (figure 1.5). t 5 2 6 t 5 2 6 s 0 0 0 s 5 2 6 (a) (b) Fig. 1.5 – (a) avant copie ; (b) apr`s copie. e1.2 R´f´rences d’objets ee Ce qui a ´t´ illustr´ sur des tableaux s’applique aussi aux objets, d´finis comme instances ee e ede classes. Reprenons la classe des « personnes » d´j` introduite plus haut. ea class Personne { String nom; int age; }On s’en sert comme dans la m´thode suivante. e public static void main(String[] args) { Personne p = new Personne(); p.nom = "Dominique"; p.age = 22; System.out.println(p);
  10. 10. ´ ´1. REFERENCES 13 System.out.println(p.nom + ", " + p.age + " ans"); }avec le r´sultat e > Personne@cac268 > Dominique, 22 ansLa premi`re ligne affich´e est le contenu de p qui est une r´f´rence ; cette r´f´rence est retourn´e e e ee ee epar l’instruction new Personne(). La deuxi`me ligne donne le contenu des champs de l’objet dont ep contient la r´f´rence. L’objet cr´´ par le programme est repr´sent´ dans la figure 1.6, avant et ee ee e eapr`s remplissage. e nom age nom age p 0 p 22 Dominique Fig. 1.6 – Personne cr´´e par new Personne(), et apr`s remplissage des champs. ee e Comme d´j` dit plus haut, null est une valeur de r´f´rence particuli`re. Ce n’est la r´f´rence ea ee e eed’aucun objet. On peut affecter null a toute variable r´f´rence, mais aucun champ et aucune ` eem´thode d’objet n’est accessible par une variable qui contient cette valeur sp´ciale. Une telle e etentative provoque la lev´e d’une exception de la classe NullPointerException. e L’op´rateur new est utilis´ pour la cr´ation d’un nouvel objet. e e e e `R`gle. A la cr´ation, les champs d’un objet sont initialis´s a une valeur par d´faut. Cette valeur e e ` eest 0 pour les types primitifs num´riques, false pour le type bool´en, et null pour tout champ e eobjet. Et en effet, le programme public static void main(String[] args) { Personne p = new Personne(); System.out.println(p + " nom: " + p.nom + ", age: " + p.age); }affiche > Personne@93dee9 nom: null, age: 0Compliquons un peu. Nous avons dit que les deux op´rations de cr´ations de nouveaux types sont e eles classes et les tableaux. Cr´ons donc un tableau de trois personnes. L` ´galement, le tableau e aecontient, au d´part, des valeurs par d´faut, donc null puisqu’il s’agit d’un tableau d’objets. e e public static void main(String[] args) { Personne[] t = new Personne[3]; Personne p = new Personne(); p.nom = "Dominique"; p.age = 22; t[1] = p; for (int i = 0; i < t.length; i++) System.out.print(t[i] + " "); }
  11. 11. 14 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATIONLe r´sultat est e > null Personne@93dee9 nullLa situation est d´crite dans la figure 1.7. e t nom age 22 Dominique pFig. 1.7 – Tableau de trois personnes. La deuxi`me entr´e est affect´e par copie de la valeur de e e ep.1.3 Constructeurs Une classe Java est un bloc d’un programme. Une classe d´finit un nouveau type non primitif, eet peut contenir – des attributs (aussi appel´s variables), e – des m´thodes, e – des initialisations, – d’autres classes (classes internes ou imbriqu´es). eParmi les m´thodes, nous connaissons les m´thodes de classe, ou m´thodes statiques (justement e e ecelles qualifi´es static) et les m´thodes d’instance. Les constructeurs sont d’autres m´thodes qui e e epermettent de cr´er des objets tout en affectant aux champs des valeurs. Leur syntaxe est un epeu particuli`re. Un constructeur e – a le mˆme nom que la classe, e – est sans type de retour (pas mˆme void !). eUn constructeur est appel´ par new. Un constructeur particulier, appel´ constructeur par d´faut, e e eest fourni a toute classe : c’est le constructeur sans arguments, celui que nous avons utilis´ ` ejusqu’alors. Il initialise tous les champs de l’objet cr´´ a leur valeur par d´faut. ee ` e Plusieurs constructeurs peuvent coexister. Ceci est dˆ a la possibilit´ de surcharger un nom u` ede m´thode : deux m´thodes sont surcharg´es (ou l’une est une surcharge de l’autre), si elles ont e e ele mˆme nom, mais diff`rent par le nombre ou par le type de leurs arguments. e e Reprenons la classe Personne. Le constructeur par d´faut initialise nom a null et age a 0. e ` `Voici une nouvelle implantation, avec deux autres constructeurs : class Personne { String nom; int age; Personne() {} Personne(String n, int a) { nom = n; age = a; }
  12. 12. ´ ´1. REFERENCES 15 Personne(String n) { nom = n; } }Le programme public static void main(String[] args) { Personne p = new Personne("Luc", 23); Personne q = new Personne("Anne"); System.out.println(p.nom + ", " + p.age + " ans."); System.out.println(q.nom + ", " + q.age + " ans."); }affiche > Luc, 23 ans. > Anne, 0 ans.Notons que le constructeur par d´faut cesse d’ˆtre fourni implicitement d`s qu’un autre construc- e e eteur est d´fini. Si l’on veut quand mˆme disposer de ce constructeur, il faut le red´finir — comme e e enous l’avons fait. Voici un autre exemple. Nous manipulons un ensemble de personnes au moyen d’un annuaire,o` on peut ajouter des personnes, en enlever, et demander si une personne y figure. La classe uAnnuaire est form´e d’un tableau de Personne et d’un entier n qui donne la taille de l’annuaire. e class Annuaire { final static int nMax = 12; Personne[] liste = new Personne[nMax]; int n = 0; ... }La constante nMax sert a donner une taille maximale. Les m´thodes a d´finir sont ` e ` e class Annuaire { ... boolean ajouter(String nom, int age) {...} boolean enlever(String nom) {...} int index(String nom) {...} boolean estDans(String nom) {...} void afficher() {...} }On peut s’en servir dans un programme comme public static void main(String[] args) { Annuaire a = new Annuaire(); a.ajouter("Luc", 22); a.ajouter("Anne", 21); a.afficher(); System.out.println();
  13. 13. 16 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATION boolean x = a.estDans("Luc"); // retourne true a.enlever("Luc"); a.afficher(); }et le r´sultat est e > Luc, 22 ans. > Anne, 21 ans. > > Anne, 21 ans.La m´thode index retourne l’entier i tel que liste[i] a pour champ nom l’argument pass´ en e eparam`tre, si un tel entier existe, et −1 sinon. Les m´thodes ajouter et enlever retournent un e ebool´en qui indique si l’op´ration a r´ussi. e e e class Annuaire { final static int nMax = 12; Personne[] liste = new Personne[nMax]; int n = 0; boolean ajouter(String nom, int age) { if (estDans(nom) || n == nMax) return false; liste[n++] = new Personne(nom, age); return true; } boolean enlever(String nom) { int i = index(nom); if (i == -1) return false; for (int j = i + 1; j < n; j++) liste[j - 1] = liste[j]; n--; return true; } int index(String nom) { for (int i = 0; i < n; i++) if (liste[i].nom.equals(nom)) return i; return -1; } boolean estDans(String nom) { return (index(nom) != -1); } void afficher() {
  14. 14. ´2. METHODES ET VARIABLES STATIQUES 17 for (int i = 0; i < n; i++) { Personne p = liste[i]; System.out.println(p.nom + ", " + p.age + " ans."); } } }Ce programme, comme beaucoup d’autres semblables, doit traiter le probl`me de l’adjonction ed’un ´l´ment a un tableau qui est d´j` plein. La politique de l’autruche consiste a ignorer ce ee ` ea `cas. Plus s´rieusement, on peut consid´rer deux solutions : traiter le probl`me, ou le signaler. e e ePour signaler le probl`me, on utilise la m´thode ci-dessus : on ne fait pas l’adjonction, et on e eretourne la valeur false. Une version plus sophistiqu´e, bas´e sur les exceptions, sera d´crite plus e e eloin. Pour traiter le probl`me du tableau plein, il n’y a gu`re qu’une solution : augmenter sa e etaille. Ceci se fait en dupliquant le tableau dans un tableau plus grand, comme d´crit ci-dessous e(la m´thode est a inclure dans la classe Annuaire) : e ` void doublerListe() { Personne[] nouvelleListe = new Personne[2*liste.length]; for (int i = 0; i < liste.length; i++) nouvelleListe[i] = liste[i]; liste = nouvelleListe; }Bien voir que l’on recopie le contenu de l’ancien tableau dans le nouveau, puis que l’on copiel’adresse du nouveau tableau dans la variable contenant l’ancienne adresse. Avec cette m´thode, ela m´thode ajouter peut se r´crire de la fa¸on suivante : e e c boolean ajouter(String nom, int age) { if (estDans(nom)) return false; if (n == liste.length) doublerListe(); liste[n++] = new Personne(nom, age); return true; }En fait, il est si souvent n´cessaire de recopier des tableaux qu’il existe une m´thode toute prˆte e e epropos´e par Java (voir la m´thode System.arraycopy()). De plus, Java dispose de classes qui e eg`rent des tableaux dont la taille augmente automatiquement en cas de besoin. e2 M´thodes et variables statiques e Comme pour les r´f´rences, la distinction entre m´thodes (ou variables) statiques et non ee estatiques ne pr´sente plus de difficult´ si on a assimil´ les principes de bases. e e e2.1 Variables statiques Une variable d´clar´e static est commune a tous les objets de la classe. Pour cette raison, e e `on parle de variable de classe et on peut l’utiliser avec la syntaxe NomdeClasse.nomDeVariable.Par exemple, dans Math.Pi, Pi est une variable final static de la classe Math. En revanche, unevariable non statique aura une instance par objet. Une variable statique peut aussi ˆtre manipul´e e e
  15. 15. 18 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATIONa travers un objet de la classe, comme toute variable. Ce faisant, on acc`de toujours a la mˆme` e ` evariable. Comme une variable statique est attach´e a une classe, et non a un objet, on a la r`gle e ` ` eessentielle suivante.R`gle. Toute modification d’une variable statique peut ˆtre faite indistinctement par n’importe e equel objet de la classe. Cette modification est visible par tous les objets de la classe. Pour illustrer ce ph´nom`ne, consid´rons une classe contenant une variable statique et une e e evariable non statique. class Paire { static int x; int y; }On peut alors utiliser la variable statique x, sous la forme Paire.x, sans d´clarer d’objet, mais e´crire Paire.y provoquerait une erreur. Par contre, si on d´clare un objet s de type Paire, on ae eacc`s aux champs s.x et s.y. e class Test { static void main() { Paire s = new Paire(); System.out.println("s.x = " + s.x + ", s.y = " + s.y + ", Paire.x = " + Paire.x); } }Le r´sultat est e > s.x = 0, s.y = 0, Paire.x = 0Modifions l´g`rement la classe Test. e e class Test { static void main() { Paire s = new Paire(); Paire t = new Paire(); t.x = 2; t.y = 3; System.out.println("t.x = " + t.x + ", t.y = " + t.y); System.out.println("s.x = " + s.x + ", s.y = " + s.y); } }Le r´sultat est e > t.x = 2, t.y = 3 > s.x = 2, s.y = 0Comme on le voit, l’instruction t.x = 2 a modifi´ la valeur de la variable statique x. De ce fait, la evaleur retourn´e par s.x est ´galement modifi´e. En revanche, l’instruction t.y = 3 n’a eu aucune e e einfluence sur la valeur de s.y. Pour clarifier de tels comportements, il est d’ailleurs pr´f´rable de eeremplacer l’instruction t.x = 2 par Paire.x = 2. Mˆme s’il y a plusieurs objets de la classe Paire, il e
  16. 16. ´2. METHODES ET VARIABLES STATIQUES 19n’y a qu’une seule instance de la variable statique x. Les expressions Paire.x, s.x et t.x d´signent ela mˆme donn´e a la mˆme adresse. Une derni`re pr´cision : a l’int´rieur de la classe Paire, la e e ` e e e ` evariable statique Paire.x peut s’´crire plus simplement x, car la classe courante est implicite. e Pour bien illustrer ce qui pr´c`de, voici une l´g`re variation du programme pr´c´dent. e e e e e e class Paire { static int[] x; int y; } class StaticTest { public static void main(String[] args) { Paire.x = new int[1]; Paire.x[0] = 7; System.out.println( "Paire.x = " + Paire.x + ", Paire.x[0] = " + Paire.x[0]); Paire s = new Paire(); s.y = 3; System.out.println("s.x = " + s.x + ", s.y = " + s.y); Paire t = new Paire(); t.y = 4; System.out.println("t.x = " + t.x + ", t.y = " + t.y); } }Ce programme affichera > Paire.x = [I@93dee9, Paire.x[0] = 7 > s.x = [I@93dee9, s.y = 3 > t.x = [I@93dee9, t.y = 42.2 M´thodes statiques e Une m´thode d´clar´e static peut ˆtre utilis´e sans r´f´rence a un objet particulier. On e e e e e ee `parle alors de m´thode de classe. Une m´thode statique n’a pas directement acc`s aux variables e e enon statiques. On peut appeler une m´thode statique par NomdeClasse.nomDeMethode(). Par eexemple, dans Math.abs(), abs() est une m´thode statique de la classe Math. e Une m´thode qui n’est pas d´clar´e static est toujours utilis´e en r´f´rence a un objet. e e e e ee `On parle alors de m´thode d’objet. On peut appeler une m´thode non statique par nomOb- e ejet.nomDeMethode(). Par exemple, la classe System contient une variable de classe de nom out.L’objet System.out appelle une m´thode d’objet de nom println(). e Pour simplifier l’´criture des m´thodes d’objet, on utilise souvent le mot cl´ this, qui d´signe e e e el’objet courant. Il n’a jamais la valeur null, et sa valeur ne peut ˆtre modifi´e. Utiliser this dans e eune m´thode statique produit une erreur a la compilation. e ` Pour illustrer ces d´finitions, introduisons simultan´ment dans la classe Paire des m´thodes e e ede classe et des m´thodes d’objet. e class Paire { static int x;
  17. 17. 20 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATION int y; void affiche_x() // m´thode d’objet e { System.out.println("s.x = " + this.x); } void affiche_y() // m´thode d’objet e { System.out.println("s.y = " + this.y); } static void affiche2_x() // m´thode de classe e { System.out.println("s.x = " + x); } static void affiche3_y(Paire s) { System.out.println("s.y = " + s.y); } }On remarquera la syntaxe utilisant this dans affiche x et dans affiche y et l’utilisation de lavariable statique x dans affiche2 x. On notera ´galement la diff´rence entre la m´thode sans e e eparam`tre affiche y et la m´thode affiche3 y qui prend en param`tre un objet s de type Paire. e e eAttention en effet au pi`ge suivant : e // Attention, cette m´thode est incorrecte ... e static void affiche2_y() { System.out.println("s.y = " + y); } // ... car y n’est pas "static" !Maintenant, le programme class Test { static void main() { Paire s = new Paire(); Paire t = new Paire(); t.x = 2; t.y = 3; s.affiche_x(); s.affiche_y(); Paire.affiche2_x(); s.affiche2_x(); Paire.affiche3_y(s); } // s.affiche3_y(s) serait maladroit }produit le r´sultat suivant : e
  18. 18. ´3. METHODES ET VARIABLES FINAL 21 > s.x = 2 > s.y = 0 > s.x = 2 > s.x = 2 > s.y = 03 M´thodes et variables final e La d´claration d’une variable final doit toujours ˆtre accompagn´e d’une initialisation. Une e e em´thode final ne peut ˆtre surcharg´e. e e e Une variable final ne peut ˆtre modifi´e. Attention toutefois ! Si la variable est un tableau, e edonc une r´f´rence, cette r´f´rence ne change plus, mais les valeurs du tableau peuvent ˆtre ee ee emodifi´es ! e class Test { static final int n = 100; static final int[] a = {1, 2, 3}; public static void main(String args[]) { a[0] = 5; // OK n = 9; // Erreur ! Variable finale } }Les m´thodes d’une classe final sont implicitement final. La classe String est une classe final. e4 La classe String La manipulation des chaˆ ınes de caract`res est simplifi´e en Java par la classe String. Cette e eclasse, dont nous venons de dire qu’elle est finale, permet les op´rations usuelles sur les chaˆ e ınes decaract`res. Toutefois, les objets de la classe String sont immutables, c’est-`-dire qu’ils ne peuvent e aˆtre modifi´s. Une op´ration sur une chaˆ de caract`re se traduit donc, lors d’une modification,e e e ıne epar la cr´ation d’un nouvel objet. Dans la suite de ce polycopi´, on parlera souvent d’op´rations e e edestructrices et non destructrices ; dans ce contexte, les op´rations de la classe String sont non edestructrices. Il existe une variante, destructrice, des chaˆ ınes de caract`res, la classe StringBuffer. e Les m´thodes de la classe String sont nombreuses. Voici quelques-unes des plus fr´quemment e eutilis´es : e int length() char charAt(int index) String toLowerCase() String toUpperCase()Ainsi "abc".charAt(1)retourne le caract`re b, et e "X2001".toLowerCase()retourne la chaˆ x2001. D’autres m´thodes sont utiles pour la comparaison, l’examen, la mo- ıne edification. En voici quelques exemples :
  19. 19. 22 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATION boolean equals(String s) int compareTo(String s) boolean startsWith(String prefix) boolean endsWith(String suffix) int indexOf(String facteur) int lastIndexOf(String facteur)L’´galit´ de deux chaˆ e e ınes de caract`res se teste par equals. Le test par == teste l’´galit´ des e e eadresses. La classe String est tr`s optimis´e en Java, et souvent les chaˆ e e ınes compos´es des mˆmes e ecaract`res sont en fait rang´es qu’une seule fois en m´moire (ce qui est possible puisqu’elles sont e e einmodifiables !). De ce fait, l’´galit´ du contenu implique souvent l’´galit´ des adresses et peut e e e edonc ˆtre test´ de cette mani`re. e e e La m´thode compareTo compare la chaˆ avec l’argument, pour l’ordre alphab´tique. Elle e ıne eretourne un entier n´gatif, 0, ou un entier positif selon que la chaˆ appelante est plus petite, e ıne´gale ou plus grande que la chaˆ pass´e en argument. Ainsie ıne e "X2001".compareTo("W1001")retourne un nombre positif. Bien entendu, compareTo retourne 0 si et seulement si equals retournetrue. La m´thode indexOf (resp. lastIndexOf) retourne le plus petit (resp. plus grand) indice o` e ula chaˆ en argument commence comme facteur de la chaˆ appelante, et -1 si l’argument n’est ıne ınepas facteur. Ainsi "X2000001".indexOf("00") "X2000001".lastIndexOf("00")donne 2 et 5. Enfin, voici quelques op´rations de construction : e String valueOf(int i) String substring(int debut, int fin) String concat(String s) String replace(char x, char y)Il existe en fait neuf versions de la m´thode valueOf, qui diff´rent par le type de l’argument e e(celle cit´e ci-dessus a pour argument un int). Elles retournent toutes une chaˆ de caract`res e ıne erepr´sentant la valeur pass´e en param`tre. La m´thode concat retourne une nouvelle chaˆ de e e e e ınecaract`res form´e de la chaˆ appelante et de la chaˆ en param`tre. Enfin, notons la possibilit´ e e ıne ıne e ede cr´er un objet de la classe String implicitement, par l’´num´ration de son ´criture. Ainsi, les e e e echaˆınes comme ”X2000001” sont des objets de la classe String. La m´thode remplace remplace etoutes les occurrences du caract`re x par le caract`re y. Le mot “remplace” n’est pas tr`s bon, e e epuis que c’est une nouvelle chaˆ de caract`res qui est cr´e. ıne e e Parfois, il est utile de disposer de la suite des caract`res d’un String sous forme d’un tableau. eLa m´thode toCharArray() retourne un tel tableau ; r´ciproquement, le constructeur String(char[] e etableau) construit une chaˆ a partir d’un tableau de caract`res. ıne ` e char[] tableau = "X2000001".toCharArray(); tableau[1]++; String s = new String(tableau);La chaˆ retourn´e est bien sˆ r ”X3000001”. ıne e u Retour sur la m´thode toString(). La m´thode toString() fournit une chaˆ de caract`res e e ıne econtenant une description textuelle de l’objet qui l’appelle. Comme nous l’avons dit plus haut,cette m´thode retourne par d´faut une chaˆ de caract`res form´e du nom de la classe, de e e ıne e e’@’ et de l’´criture hexad´cimale du code de hachage de l’objet. Pour les objets d´finis par des e e etableaux, le nom est form´ du pr´fixe [ pour les tableaux, et du codage suivant du nom des e etypes :
  20. 20. 4. LA CLASSE STRING 23 B byte J long C char Lnom de classe ; objet D double S short F float Z boolean I int Consid´rons l’exemple suivant : e class Exemple { public static void main(String[] args) { Exemple e = new Exemple(); Exemple[] t = new Exemple[12]; Exemple[][] u = new Exemple[12][24]; System.out.println(e) ; System.out.println(t) ; System.out.println(u) ; } }Le r´sultat est : e > Exemple@7182c1 > [LExemple;@3f5d07 > [[LExemple;@fabe9et en effet, la variable u est un tableau dont les ´l´ments sont des tableaux dont les ´l´ments ee eesont de type Exemple.
  21. 21. 24 ´ CHAPITRE I. COMPLEMENTS DE PROGRAMMATION
  22. 22. Chapitre IIStructures s´quentielles e Les structures de donn´es en algorithmique sont souvent complexes et de taille variable. eElles sont souvent aussi dynamiques, au sens o` elles ´voluent, en forme et en taille, en cours u ed’ex´cution d’un programme. Les tableaux pr´sentent, dans cette optique, un certain nombre e ede limitations qui en rendent parfois l’usage peu naturel. Tout d’abord, un tableau est de taille fixe, d´termin´ a sa cr´ation. Il faut donc choisir une e e` etaille suffisante d`s le d´but, ce qui peut amener a un gaspillage consid´rable de place. Et mˆme e e ` e esi l’on a vu large, il arrive que la place manque. Il faut alors proc´der a la cr´ation d’un tableau e ` eplus grand et a une recopie, ce qui repr´sente une perte de temps qui peut ˆtre importante. ` e e Un tableau a une structure fixe, lin´aire. Une modification de l’ordre, par exemple par inser- etion d’un ´l´ment, oblige a d´placer d’autres ´l´ments du tableau, ce qui peut ˆtre tr`s coˆ teux. ee ` e ee e e uUn tableau est donc rigide, et peu ´conomique si l’ensemble des ´l´ments qu’il contient est appel´ e ee ea ´voluer en cours d’ex´cution.`e e Dans ce chapitre et dans le chapitre suivant, nous introduisons quelques structures dyna-miques. Elles sont utilis´es de fa¸on tr`s intensive en programmation. Leur but est de g´rer un e c e eensemble fini d’´l´ments dont le nombre n’est pas fix´ a priori et peut ´voluer. ee e e Les structures dynamiques doivent r´pondre a un certain nombre de crit`res. Elles doivent e ` epermettre une bonne gestion de la m´moire, par un usage ´conome de celle-ci. Mais elles doivent e eaussi ˆtre simples a utiliser, pour ne pas ´changer cette ´conomie en m´moire contre une difficult´ e ` e e e ed’emploi, ou un temps d’ex´cution disproportionn´. e e La solution pr´sent´e ici, et g´n´ralement utilis´e, repose sur trois principes : e e e e e – Allocation de la m´moire au fur et a mesure des besoins. On dispose alors de toute la place e ` n´cessaire sans en gaspiller. e – D´finition r´cursive des structures. Elle permettent en contrepoint une programmation e e simple par des m´thodes elle-mˆmes r´cursives, calqu´es sur leur structure. e e e e – Enfin, un petit nombre d’op´rateurs d’acc`s et de modification, a partir desquels on e e ` construit des m´thodes plus ´labor´es par composition. e e eAllouer de la m´moire signifie r´server de la m´moire pour l’utilisation du programme. Apr`s e e e eutilisation, on peut ensuite d´sallouer cette m´moire. Dans des langages tels que PASCAL ou C, e eil faut g´rer soigneusement l’allocation et la d´sallocation. En JAVA, c’est un peu plus simple : e eil faut toujours allouer de la m´moire, mais JAVA se charge de d´sallouer la m´moire qui ne sert e e eplus. On notera que la description ci-dessus repose sur des principes qui ne d´pendent pas du lan- egage de programmation choisi. En Java, et dans d’autres langages de haut niveau, on peut mˆme esp´cifier une structure dynamique par une description purement abstraite de ses propri´t´s. Ce e eestyle de programmation utilisant des types abstraits rel`ve des cours ult´rieurs (cours 431 et e ecours de majeure). Nous ´tudierons dans ce cours deux types de structures dynamiques. Ce chapitre est consacr´ e e 25
  23. 23. 26 ´ CHAPITRE II. STRUCTURES SEQUENTIELLESaux structures dites s´quentielles : listes, piles et files d’attente. Le chapitre suivant traite des earbres et de leur utilisation. D’autres structures dynamiques, en particulier les graphes, sonttrait´es dans le cours « Fondements de l’informatique ». e Les structures consid´r´es servent a manipuler des ensembles. Les ´l´ments de ces ensembles ee ` eepeuvent ˆtre de diff´rentes sortes : nombres entiers ou r´els, chaˆ e e e ınes de caract`res, ou des objets eplus complexes comme des processus, des expressions, des matrices. Le nombre d’´l´ments des eeensembles varie au cours de l’ex´cution du programme, par ajout et suppression d’´l´ments en e eecours de traitement. Plus pr´cis´ment, les op´rations que l’on s’autorise sur un ensemble E sont e e eles suivantes : – tester si l’ensemble E est vide. – ajouter l’´l´ment x a l’ensemble E. ee ` – v´rifier si l’´l´ment x appartient a l’ensemble E. e ee ` – supprimer l’´l´ment x de l’ensemble E. ee1 Listes chaˆ ees ın´ Une liste chaˆn´e est compos´e d’une suite finie de couples form´s d’un ´l´ment et de l’adresse ı e e e ee(r´f´rence) vers l’´l´ment suivant. ee ee Il s’agit d’une structure de base de la programmation, fort ancienne. Un usage syst´matique een est d´j` fait dans le langage Lisp (LISt Processing), con¸u par John MacCarthy en 1960. ea cUne version plus r´cente de ce langage, Scheme, utilise principalement cette structure. Les listes esont aussi pr´d´finies en Caml. Dans la plupart des langages a objets, comme C++ et Java, on e e `trouve des classes g´rant les listes. e La recherche d’un ´l´ment dans une liste s’apparente a un classique « jeu de piste » dont le ee `but est de retrouver un objet cach´ : on commence par avoir des informations sur un « site » o` e upourrait se trouver cet objet. En ce lieu, on d´couvre des informations, et une indication sur un eautre site (l’adresse d’un autre site) o` l’objet pourrait se trouver, et ainsi de suite. Le point de ud´part du jeu de piste est l’adresse du premier site qu’il faut visiter. Une version contemporaine ede ce jeu consisterait en une suite de pages Web contenant une information et un lien vers lapage suivante. Il y a une analogie ´vidente entre lien et r´f´rence. e ee Notons que l’ordre de la suite des ´l´ments d’une liste chaˆ ee n’est pas connu globalement, ee ın´puisqu’on ne connaˆ que le successeur d’un ´l´ment. C’est ce caract`re local qui permet une ıt ee egrande souplesse d’organisation : il suffit de modifier, en un seul site, la r´f´rence au site suivant eepour r´organiser la suite des pages Web. e Les op´rations usuelles sur les listes sont les suivantes : e – cr´er une liste vide. e – tester si une liste est vide. – ajouter un ´l´ment en tˆte de liste. ee e – rechercher un ´l´ment dans une liste. ee – supprimer un ´l´ment dans une liste. eeD’autres op´rations sont : retourner une liste, concat´ner deux listes, fusionner deux listes, etc. e e En Java, les « sites » sont des objets qui poss`dent deux champs : un champ pour le contenu, eet un champ qui contient la r´f´rence vers le site suivant. L’acc`s a la liste se fait par la r´f´rence ee e ` eeau premier site. Il est de tradition d’appeler cellule ce que nous avons appel´ site. e1.1 D´finitions e Les d´clarations sont les suivantes. On suppose ici que les ´l´ments stock´s dans la liste sont e ee edes entiers. Bien entendu, le sch´ma de d´claration serait le mˆme pour une liste de caract`res, e e e ede bool´ens, ou d’objets de type quelconque. e
  24. 24. 1. LISTES CHAˆ EES IN ´ 27 class Liste { int contenu; Liste suivant; Liste (int x, Liste a) { contenu = x; suivant = a; } }Une liste est repr´sent´e par la r´f´rence au premier objet de type Liste. La liste vide, sans e e ee´l´ment, a pour r´f´rence null. La figure 1.1 repr´sente la forme g´n´rale d’une liste. La liste estee ee e e ecompos´e de cellules. Chacune contient, dans son champ contenu, l’un des ´l´ments de la liste. e eeLe champ suivant contient la r´f´rence a la cellule suivante. La croix dans la derni`re cellule ee ` eindique que sa valeur est null, convention pour indiquer qu’il n’y a pas d’´l´ment suivant. Une eeliste est rep´r´e par la r´f´rence a sa premi`re cellule. ee ee ` e su nu su nu su nu nt nt nt e iva e iva e iva nt nt nt co co co ¢¢¡ ¡ ¡ Fig. 1.1 – Forme g´n´rale d’une liste. e e L’instruction new Liste (x, a) construit une nouvelle cellule dont les champs sont x et a etretourne la r´f´rence a cette cellule. Revenons a notre jeu de piste. Supposons que l’adresse du ee ` `premier site soit a. L’instruction new Liste (x, a) revient a ins´rer, au d´but du jeu, un nouveau ` e esite, dont le contenu est x. Il contient, comme indication du site suivant, l’adresse de l’ancienpoint de d´part, soit a. Le nouveau point de d´part est le site fraˆ e e ıchement ins´r´. ee Cr´er une liste de trois entiers, par exemple de 2, 7 et 11 se fait par usage r´p´t´ de l’ins- e e eetruction d’insertion : Liste a = new Liste(2, new Liste (7, new Liste (11, null)))La liste de cet exemple est repr´sent´e dans la figure 1.2. e e a 2 7 11 Fig. 1.2 – Une liste de trois ´l´ments 2, 7, 11 repr´sent´es par trois cellules. ee e e Venons-en aux op´rations sur les listes. La m´thode listeVide retourne une liste vide, c’est- e ea-dire null.` static Liste listeVide () { return null; }
  25. 25. 28 ´ CHAPITRE II. STRUCTURES SEQUENTIELLESLa fonction qui teste si une liste est vide s’´crit : e static boolean estVide (Liste a) { return a == null; }Il faut bien dire que ces fonctions sont si simples qu’on les remplace souvent par leur d´finition edans la pratique. Ajouter un ´l´ment en d´but de liste se fait par : ee e static Liste ajouter (int x, Liste a) { return new Liste (x, a); }L’effet de cette op´ration est repr´sent´ dans la figure 1.3. e e e a ¤¢£ £ £ x Fig. 1.3 – Ajout d’un ´l´ment x au d´but d’une liste a. ee e Ainsi, cr´er une liste contenant seulement x se fait de l’une des quatre fa¸ons ´quivalentes : e c e new Liste(2, null) ajouter(x, null) new Liste(2, listeVide()) ajouter(x, listeVide())Voici un autre exemple : static Liste suite(int n) { Liste a = null; for (int i = n; i >=1; i--) a = ajouter(i, a); return a; }Cette fonction retourne une liste contenant les entiers de 1 a n, dans cet ordre. Noter le parcours `d´croissant des entiers dans la boucle. e Deux fonctions d’acc`s sont importantes : celle retournant le contenu, et celle retournant la er´f´rence a la suite de la liste. Ces fonctions ne doivent ˆtre appel´es que sur des listes non vides. ee ` e e static int tete(Liste a) { return a.contenu; } static Liste queue(Liste a) { return a.suivant; }
  26. 26. 1. LISTES CHAˆ EES IN ´ 29L` encore, on utilise souvent dans la pratique l’expression plutˆt que la fonction. Les trois a ofonctions ajouter, tete et queue sont li´es par les ´quations suivantes. e e tete(ajouter(x, a)) = x queue(ajouter(x, a)) = a ajouter(tete(a), queue(a)) = a; (a = null)Elles permettent de simplifier toute expression compos´e de ces trois fonctions en une forme er´duite. e Les trois fonctions ajouter, tete et queue permettent de r´aliser beaucoup d’op´rations sur e eles listes. Elles ne permettent pas, en revanche, de modifier la valeur d’un champ, que ce soitle contenu ou la r´f´rence a la cellule suivante. C’est pourquoi on appelle ces fonctions, et ee `les op´rations qu’elles permettent de r´aliser, des op´rations non destructives, en opposition e e eaux op´rations qui modifient la valeur, qui d´truisent donc les valeurs pr´c´dentes, et qui sont e e e eappel´es destructives. L’usage d’op´rations non destructives pour modifier une donn´e am`ne a e e e e `la recopie des parties a modifier. `1.2 Fonctions simples Voici quelques exemples de fonctions simples et utiles. Elles sont bas´es sur le parcours des e´l´ments d’une liste. La longueur d’une liste est le nombre d’´l´ments qui la composent. En voiciee eeune r´alisation : e static int longueur(Liste a) { if (estVide(a)) return 0; return 1 + longueur(queue(a)); }On peut l’´crire de mani`re plus condens´e, en utilisant une expression conditionnelle : e e e static int longueur(Liste a) { return (estVide(a)) ? 0 : 1 + longueur(queue(a)); }On peut aussi l’´crire de mani`re it´rative, et par acc`s direct aux champs : e e e e static int longueurI(Liste a) { int n = 0; while (a != null) { n++; a = a.suivant; } return n; }Autre exemple similaire, l’affichage d’une liste. On choisit ici d’afficher les ´l´ments sur une eeligne, s´par´s par un blanc. e e static void afficher(Liste a) { if (estVide(a)) System.out.println();
  27. 27. 30 ´ CHAPITRE II. STRUCTURES SEQUENTIELLES else { System.out.print(a.contenu + " "); afficher(a.suivant); } }Notons que si les deux derni`res instructions de la m´thode pr´c´dente sont interverties, la liste e e e eest affich´e dans l’ordre inverse ! Bien sˆ r, on peut ´crire notre fonction de fa¸on it´rative : e u e c e static void afficherI(Liste a) { while (a != null) { System.out.print(a.contenu + " "); a = a.suivant; } System.out.println(); }1.3 Op´rations eCopie La copie d’une liste est facile a r´aliser. En voici une version r´cursive. ` e e static Liste copier(Liste a) { if (a == null) return a; return ajouter(tete(a), copier(queue(a)); }Recherche La recherche d’un ´l´ment dans une liste peut aussi se faire it´rativement et r´cursivement. ee e eC’est encore un algorithme bas´ sur un parcours, mais le parcours est interrompu d`s que e el’´l´ment cherch´ est trouv´. Voici l’´criture it´rative : ee e e e e static boolean estDansI(int x, Liste a) // it´ratif e { while (a != null) { if (a.contenu == x) return true; a = a.suivant; } return false; }La boucle while peut ˆtre transform´e de mani`re m´canique en une boucle for. Dans notre cas, e e e eon obtient l’´criture suivante : e static boolean estDansI(int x, Liste a) // it´ratif e { for(; a!= null; a = a.suivant)
  28. 28. 1. LISTES CHAˆ EES IN ´ 31 if (a.contenu == x) return true; return false; }Voici enfin une premi`re version r´cursive. Elle distingue trois cas : une liste vide, o` la recherche e e u´choue, une liste dont la tˆte est l’´l´ment cherch´, o` la recherche r´ussit, et le cas restant, trait´e e ee e u e epar r´cursion. e static boolean estDans(int x, Liste a) // r´cursif e { if (a == null) return false; if (a.contenu == x) return true; return estDans(x, a.suivant); }Voici une deuxi`me version, utilisant a la fois les fonctions d’acc`s et une expression bool´enne : e ` e e static boolean estDans(int x, Liste a) // r´cursif e { return !estVide(a) && (tete(a) == x || estDans(x, queue(a))); }En Java, l’´valuation d’expressions bool´ennes se fait de gauche a droite et est toujours pares- e e `seuse. Ceci signifie que lors de l’´valuation d’une expression bool´enne du type b1 || b2, o` b1 e e uet b2 sont des expressions bool´ennes, Java ´value d’abord b1. Si le r´sultat de cette ´valuation e e e eest true, il en d´duit que la valeur de b1 || b2 est true, sans mˆme ´valuer b2. Par contre, si le e e er´sultat est false, il poursuit en ´valuant b2. Dans le cas d’une expression du type b1 && b2, le e emˆme principe paresseux s’applique : si b1 est ´valu´ a false, Java en conclut que b1 && b2 vaut e e e`false sans ´valuer b2. e Dans notre cas, le deuxi`me facteur de la conjonction n’est donc pas ´valu´ si la liste est e e evide. On voit donc l` un int´rˆt subtil de l’´valuation paresseuse, en sus du gain de temps. Si a ee el’´valuation n’´tait pas paresseuse, comme c’´tait le cas en PASCAL, un des ancˆtres de JAVA, e e e el’´valuation de tete(a) provoquerait une erreur... eSuppression La suppression d’un ´l´ment x dans une liste consiste a ´liminer la premi`re cellule — si elle ee `e eexiste — qui contient cet ´l´ment x (voir figure 1.4). Cette ´limination s’effectue en modifiant la ee evaleur du champ suivant du pr´d´cesseur : le successeur du pr´d´cesseur de la cellule de x devient e e e ele successeur de la cellule de x. Un traitement particulier doit ˆtre fait si l’´l´ment a supprimer e ee `est le premier ´l´ment de la liste, car sa cellule n’a pas de pr´d´cesseur. Nous commen¸ons par ee e e c a a x x (i) (ii)Fig. 1.4 – Suppression d’un ´l´ment x dans une liste ; (i) avant suppression, (ii) apr`s suppres- ee esion.une ´criture it´rative. e e

×