Explorer les Livres électroniques
Catégories
Explorer les Livres audio
Catégories
Explorer les Magazines
Catégories
Explorer les Documents
Catégories
TD n◦9 - Correction
Listes
Une liste est une structure permettant de stocker des éléments, un peu à la manière d’un
tableau. Une liste est composée d’éléments, un élément étant constituée d’une valeur (valeur que
l’on souhaite stocker) et d’une référence vers un autre élément. Une liste est donc une chaine
d’élément (d’où le terme liste chainée).
Dans toute la suite du TD, nous allons travailler avec les 2 classes suivantes :
class Liste{
public Element debut;
public Liste()
{
debut=null;
}
...
}
class Element{
public int valeur;
public Element suivant;
...
}
Liste
debut
1
Element e=debut;
System.out.println(e.valeur);
while(e != null)
{
e=e.suivant;
System.out.println(e.valeur);
}
}
les instructions
Element e=debut;
while(e != null)
{
e=e.suivant;
}
sont le noyau central de toutes les méthodes itératives permettant de parcourir une liste.
3. Dans la classe Element, écrire une méthode récursive permettant d’afficher l’élément cou-
rant et tous ses successeurs.
Correction :
pour afficher l’élément courant et tous ses successeurs, il faut :
(a) afficher l’élément courant.
(b) afficher l’élément suivant et tous ses successeurs (s’il y a un élément suivant.)
on a donc la méthode récursive suivante, dans la classe Element.
void afficheRec()
{
System.out.println(e.valeur);
if (suivant != null)
suivant.afficheRec();
}
Muni de cette méthode, nous pouvons écrire d’une autre façon la méthode d’affichage de la classe
Liste :
void affiche()
{
if (debut==null) return;
debut.afficheRec();
}
2
attention, il faut tout de même bien vérifier que debut n’est pas null, sinon, on aurait une erreur
à l’exécution en appelant debut.afficheRec();.
Question subsidiaire, que se passerait-il si on remplaçait afficheRec() par la méthode suivante :
void afficheRec2()
{
if (suivant != null)
suivant.afficheRec();
System.out.println(e.valeur);
}
4. Dans la classe Liste, écrire une méthode int longueur() renvoyant la longeur de la liste.
Correction :
Nous pouvons faire ça itérativement :
int longueur()
{
if (debut==null) return 0;
//la liste est vide...
Element e=debut;
int c=1;
while(e != null)
{
e=e.suivant;
c++;
}
return c;
}
ou itérativement, en remarquant que la longueur d’une liste, c’est 1 plus la longueur de la liste
partant de l’élément suivant.
//dans la classe Element
int longueurRec()
{
if (suivant==null) return 1;
return 1+suivant.longueurRec();
}
...
Dans toutes les questions suivantes, nous travaillerons maintenant dans la classe Liste.
3
5. Écrire une méthode void supprDebut() supprimant le premier élément de la liste.
Correction :
Repartons de l’exemple donné au début :
Liste
debut
Supprimer le premier élément revient juste à modifier le chainage des éléments de la liste, pour
aboutir au schéma suivant :
Liste
debut
L’ancien premier élément n’est alors plus accessible, et java va le supprimer tout seul :
Liste
debut
1 suivant 2 null
L’opération de suppression au début d’une liste s’effectue en temps constant, alors qu’elle serait
linéaire dans le cas d’un tableau (il faut créer un tableau plus petit, recopier le tableau courant
dedans sans le premier élément. . .).
4
6. Écrire une méthode void ajoutDebut(int i) ajoutant un élément au début de la liste
de valeur i.
Correction :
Ici, si l’on rajoute 0 par exemple, on veut aboutir au schéma suivant :
Liste
0 suivant
debut
Liste
0 suivant
debut
(b) refaire les chainages correctement. Sur schéma il y a deux flêches à mettre en place : il faut
que debut référence le nouveau premier élément e, et que e.suivant référence l’ancien premier
élément. Allons-y :
debut=e;
Liste
0 suivant
debut
et là, c’est le drame : nous n’avons plus de moyen d’accéder à l’ancien premier élément (celui
qui contient 3). On voudrait écrire e.suivant=quelquechose, mais il n’y a plus moyen que
quelquechose référence le bon Element.
5
Il y a deux solutions pour remédier à ça :
– On peut garder une référence vers l’ancien premier élément, pout pouvoir y accéder au
moment venu :
Element a=debut;
debut=e;
Liste
0 suivant
debut
Liste
0 suivant
debut
Element a=debut;
debut=e;
e.suivant=a;
}
6
– On peut aussi bouger les flêches dans l’autre ordre :
e.suivant=debut;
Liste
0 suivant
debut
et maintenant
debut=e;
Liste
0 suivant
debut
void ajoutDebut(int i)
{
Element e=new Element();
e.valeur=i;
e.suivant=debut;
debut=e;
}
Dans tous les cas, l’opération d’insertion au début d’une liste s’effectue en temps constant, alors
qu’elle serait linéaire dans le cas d’un tableau.
7
7. Écrire une méthode int val(int i) qui renvoie la valeur du ième élément de la liste.
Quelle est sa complexité ? Quels sont les avantages et inconvénients de l’utilisation d’une
liste par rapport à un tableau ?
Correction :
L’accés direct au ième élément n’est pas possible : il faut passer par tous les éléments précédents.
On peut faire ça itérativement :
int val(int i)
{
if (i > longueur) return -1;
//il faudrait renvoyer une erreur...
Element e=debut;
return e.valeur;
}
ou récursivement. Retourner le ième élément à partir de l’élément courant, c’est retourner le i-1ème
à partir de l’élément suivant. Avec la bonne condition d’arrêt en plus, celà donne :
//dans la classe Element
int valRec(int i)
{
if (i==1) return valeur;
return suivant.valRec(i-1);
}
...
return debut.valRec(i);
}
L’accés à un élément quelconque est donc plus couteux que dans le cas d’un tableau.
Il y donc des cas où on aura intérêt à utiliser listes plutôt que tableaux (beaucoup d’ajouts
d’éléments, peu d’accès aléatoires), et des cas où ce sera l’inverse.
8
Exercice 2 Listes doublement chainées
Insérer et supprimer à la fin d’une liste simplement chainée est une opération couteuse et
peu naturelle. Nous allons donc de nouvelles classes permettant de décrire des listes doublement
chainées :
class ListeD{
public ElementD debut;
public ElementD fin;
...
}
class ElementD{
public int valeur;
public ElementD suivant;
public ElementD precedent;
...
}
fin.precedant.suivant=null;
//fin.precedant est l’avant-dernier élément
fin=fin.precedant;
}
9
2. Écrire une méthode void ajoutFin(int i) ajoutant un élément à la fin de la liste de
valeur i.
Correction :
void ajoutFin(int i)
{
ElementD e=new ElementD();
e.valeur=i;
//liste vide
if (fin == null)
{
debut=e;
fin=e;
e.suivant=null;
e.precedant=null;
return;
}
e.precedant=fin;
fin.precedant.suivant=e;
fin=e;
}
10
3. Écrire une méthode void inserer(int i, int v) qui insère un élément de valeur v en
ième position.
Correction :
Un peu plus pénible à écrire très proprement, il y a plein de cas à vérifier. . . Je vais supposer que
nous avons écrit des méthodes pour calculer la longueur et insérer au début.
void inserer(int i, int v)
{
// cas particuliers
if (i<=1) {ajoutDebut(v); return;}
if (i>longueur()) {ajoutFin(v); return;}
Element e=debut;
Element tmp1=e;
Element tmp2=e.suivant;
}
11