Академический Документы
Профессиональный Документы
Культура Документы
Listes chanes
1.1
La notion de liste
Une liste est une structure de donnes qui permet de stocker une squence dobjets dun mme
type. En cela, les listes ressemblent aux tableaux. La squence dentiers 3, 7, 2, 4 peut tre reprsente
la fois sous forme de tableau ou de liste. La notation [3, 7, 2, 4] reprsentera la liste qui contient
cette squence.
Il y a cependant des diffrences fondamentales entre listes et tableaux :
Dans un tableau on a accs immdiat nimporte quel lment par son indice (accs dit alatoire), tandis que dans une liste chane on a accs aux lments un aprs lautre, partir du
premier lment (accs dit squentiel).
Un tableau a une taille fixe, tandis quune liste peut augmenter en taille indfiniment (on peut
toujours rajouter un lment une liste).
Dfinition (rcursive) :
En considrant que la liste la plus simple est la liste vide (note [ ]), qui ne contient aucun lment,
on peut donner une dfinition rcursive aux listes chanes dlments de type T :
la liste vide [ ] est une liste ;
si e est un lment de type T et l est une liste dlments de type T, alors le couple (e, l) est
aussi une liste, qui a comme premier lment e et dont le reste des lments ( partir du second)
forment la liste l.
Cette dfinition est rcursive, car une liste est dfinie en fonction dune autre liste. Une telle
dfinition rcursive est correcte, car une liste est dfinie en fonction dune liste plus courte, qui contient
un lment de moins. Cette dfinition permet de construire nimporte quelle liste en partant de la liste
vide.
Conclusion : la liste chane est une structure de donnes rcursive.
La validit de cette dfinition rcursive peut tre discute dun point de vue diffrent. Elle peut
tre exprime sous la forme suivante : quelque soit la liste l considre,
soit l est la liste vide [ ],
soit l peut tre dcompose en un premier lment e et un reste r de la liste, l=(e, r).
Cette dfinition donne une dcomposition rcursive des listes. La condition gnrale darrt de
rcursivit est pour la liste vide. Cette dcomposition est valide, car la liste est rduite chaque pas
une liste plus courte dune unit. Cela garantit que la dcomposition mne toujours une liste de
longueur 0, la liste vide (condition darrt).
1
1.2
Il y a plusieurs faons de reprsenter les listes chanes en Java. Lide de base est dutiliser un
enchanement de cellules :
chaque cellule contient un lment de la liste ;
chaque cellule contient une rfrence vers la cellule suivante, sauf la dernire cellule de la liste
(qui contient une rfrence nulle) ;
la liste donne accs la premire cellule, le reste de la liste est accessible en passant de cellule
en cellule, suivant leur enchanement.
La figure 1.1 illustre cette reprsentation pour la liste exemple ci-dessus [3, 7, 2, 4].
liste
Cependant, cette reprsentation ne fonctionne que pour les listes non vide (un ElementListe
contient forcment au moins une valeur).
On reprsente donc dhabitude les listes par deux classes : une classe pour les lments, et une
classe pour la liste elle-mme, qui contient une rfrence vers son premier lment.
class Liste {
ElementListe premier;
}
1.3
Nous tudierons les oprations les plus importantes sur les listes chanes, qui seront implmentes comme des mthodes de la classe Liste.
Conformment leur dfinition, les listes sont des structures rcursives. Par consquent, les oprations sur les listes sexpriment naturellement par des algorithmes rcursifs. En mme temps, les
2
c
NFA031
CNAM
2012
CHAPITRE 1. LISTES1.3.
CHANES
OPRATIONS SUR LES LISTES CHANES, VERSION ITRATIVE
listes sont des structures linaires, parfaitement adaptes un parcours itratif. Nous allons proposer dabord une version des classes qui implmentera les mthode de manire itrative, et nous en
donnerons ensuite une version rcursive.
La reprsentation de la classe Liste ci-dessous montre sous forme de mthodes les oprations
sur listes que nous discuterons.
public class Liste {
public boolean estVide() {}
public ElementListe getDebut() {}
public void ajouterAuDebut(int v) {}
public int getLongueur() {}
public boolean contient(int v) {}
public void retirerPremiereOccurrence(int v) {}
public void concatener(Liste l) {}
}
1.3.1
La classe ElementListe
Nous avons dcid de placer toute la logique des mthodes dans la classe Liste. Ce nest pas
forcment la seule solution, mais le code obtenu sera plus facile expliquer.
La classe ElementListe est donc rduite sa plus simple expression : un lment de liste a
une valeur, et est suivi dun autre lment de liste (qui peut tre ventuellement null si notre lment
est le dernier).
La classe est dote de plusieurs constructeurs et des accesseurs ncessaires.
public class ElementListe {
private int valeur;
private ElementListe suivant;
public ElementListe(int valeur, ElementListe suivant) {
this.valeur = valeur;
this.suivant = suivant;
}
/**
*Cre un lment de liste sans successeur.
*@param v
*/
public ElementListe(int v) {
this.valeur = v;
this.suivant = null;
}
public int getValeur() {
return valeur;
}
public void setValeur(int valeur) {
this.valeur = valeur;
}
c
NFA031
CNAM
2012
1.3.2
Pour ces oprations il ny a pas de variantes itratives et rcursives, laction est ralise directement sur lobjet Liste.
Obtenir le premier lment de la liste
Cette opration est ralise par la mthode getPremier() qui retourne le premier ElementListe
de la liste.
4
c
NFA031
CNAM
2012
CHAPITRE 1. LISTES1.3.
CHANES
OPRATIONS SUR LES LISTES CHANES, VERSION ITRATIVE
1.3.3
Ces oprations ncessitent un parcours complet ou partiel de la liste. Dans la variante itrative, le
parcours est ralis laide dune rfrence qui part de la premire cellule de la liste et suit lenchanement des cellules.
La figure 1.2 illustre le principe du parcours itratif dune liste laide dune rfrence ref.
........
ref
ref
liste
e1
...
ei
...
en
c
NFA031
CNAM
2012
Remarque : La rfrence ref est positionne au dbut sur la premire cellule de la liste et avance
jusqu ce quelle devient nulle la fin de la liste. Chaque fois quelle pointe vers une nouvelle
cellule, le compteur est incrment. Remarquez que lavancement dans la liste se fait en utilisant
la mthode getSuivant de la cellule courante.
Vrifier lappartenance dun lment une liste
Cette opration, ralise par la mthode contient, ncessite un parcours partiel de la liste pour
chercher llment en question. Si llment est retrouv, le parcours sarrte ce moment-l, sinon il
continue jusqu la fin de la liste.
public boolean contient(int v) {
boolean trouve= false;
ElementListe ref= getPremier();
while (! trouve && ref != null) {
if (ref.getValeur() ==v ) {
trouve= true;
} else {
ref= ref.getSuivant();
}
}
// trouve est vrai implique donc ref.getValeur() ==v
// autre test possible pour la boucle
// while (ref != null && ref.getValeur() != v)
// expliquer lordre des test dans ce cas.
return trouve;
}
c
NFA031
CNAM
2012
CHAPITRE 1. LISTES1.3.
CHANES
OPRATIONS SUR LES LISTES CHANES, VERSION ITRATIVE
// le dernier lment, celui dont le suivant est null.
ElementListe dernier= this.getPremier();
while (dernier.getSuivant() != null) {
dernier= dernier.getSuivant();
}
// Nous y sommes. dernier correspond au dernier lment
// de la liste, qui existe car celle-ci nest pas vide.
// On fixe donc le suivant de dernier au premier
// lment de la liste l.
dernier.setSuivant(l.getPremier());
}
}
Remarque : La condition de la boucle while change ici, car on veut sarrter sur la dernire cellule et
non pas la dpasser comme dans les mthodes prcdentes. Remarquez que lenchanement des
deux listes se fait en modifiant la variable dinstance suivant de la dernire cellule laide
de la mthode setSuivant.
Dans le mme esprit que lalgorithme de concatnation, on peut crire lajout dun lment en
dernire position dune liste :
public void ajouterALaFin(int v) {
if (estVide()) {
premier= new ElementListe(v);
} else {
// Il y a un dernier lment.
// On le cherche et on ajoute aprs lui.
ElementListe dernier = getDernierElement();
// nous savons que
// dernier.getSuivant() == null => dernier est bien le dernier lment.
dernier.setSuivant(new ElementListe(v));
}
}
/**
*Trouve le dernier lment dune liste non vide.
*Lance une {@link NullPointerException} pour une liste vide.
*@return le dernier lment dune liste
*@throws une NullPointerException si la liste est vide
*/
private ElementListe getDernierElement() {
ElementListe dernier= premier;
while (dernier.getSuivant() != null) {
dernier= dernier.getSuivant();
}
return dernier;
}
(la mthode getDernierElement pourra tre rutilise avec profit dans concatener pour simplifier son criture).
c
NFA031
CNAM
2012
pred
liste
ref
pred
ref
liste
e2
...
e1
...
ei
...
nouvelle liste
F IGURE 1.3 Suppression dune cellule de la liste
Le parcours de la liste la recherche de llment donn a une particularit : si la rfrence
sarrte sur la cellule qui contient llment, la suppression nest plus possible, car on na plus accs
au prdcesseur (dans les listes chanes on ne peut plus revenir en arrire).
La mthode utilise est alors de parcourir la liste avec deux rfrences :
une (elt) qui cherche la cellule liminer ;
une autre (precedent) qui se trouve toujours sur le prdcesseur de elt.
public void retirerPremiereOccurrence(int v) {
// On limine le problme de la liste vide
if (estVide())
return;
// Le but est de trouver llment qui prcde v...
// qui nexiste pas si v est la premire valeur=>
if (premier.getValeur() == v) {
premier= premier.getSuivant();
} else {
ElementListe precedent= premier;
ElementListe elt= premier.getSuivant();
while (elt != null && elt.getValeur() != v) {
precedent= elt;
elt= elt.getSuivant();
}
if (elt != null) {
// Llment a t trouv
// Plomberie:
precedent.setSuivant(elt.getSuivant());
}
}
c
NFA031
CNAM
2012
CHAPITRE 1. LISTES1.3.
CHANES
OPRATIONS SUR LES LISTES CHANES, VERSION ITRATIVE
1.3.4
Ces oprations sont bases sur une dcomposition rcursive de la liste en un premier lment et le
reste de la liste (voir la figure 1.4). Le cas le plus simple (condition darrt) est la liste avec une seule
cellule.
liste
premier reste
...
Dans notre exemple, la n vraie z fonction rcursive est la seconde. Mais comme il nest pas pratique pour le programmeur qui utilise la classe dcrire
int l= maListe.getLongueurRec(maListe.getPremier());
on cache lappel rcursif en fournissant la mthode publique getLongueur() qui n amorce z lappel, le travail tant ensuite ralis par la mthode rcursive.
Vrification rcursive de lappartenance une liste
La formule rcursive est :
contient(listeElements, e) = false si listeElements == null
true si listeElements != null
et listeElements.valeur == e
contient(listeElements.suite, e) sinon
Remarquez que dans ce cas il y a deux conditions darrt : quand on trouve llment recherch
ou quand la liste est vide. La fonction rcursive contient scrit alors comme suit :
c
NFA031
CNAM
2012
Ici encore, la fonction rcursive est la seconde, la pemire tant une faade plus agrable dutilisation.
Concatnation rcursive de deux listes
public void concatener(Liste l) {
if (this.estVide()) {
this.premier= l.premier;
} else {
concatenerRec(premier,l.getPremier());
}
}
/**
*Mthode appele uniquement si l0 est non null.
*/
private void concatener(ElementListe l0, ElementListe l1) {
if (l0.getSuivant() == null) {
l0.setSuivant(l1);
} else {
concatenerRec(l0.getSuivant(), l1);
}
}
c
NFA031
CNAM
2012
Si le premier lment de la liste est celui recherch, le rsultat sera le reste de la liste. Sinon,
le rsultat sera une liste qui aura le mme premier lment, mais dans la suite de laquelle on aura
supprim la valeur cherche.
public void retirerPremiereOccurrence(int v) {
// On limine le problme de la liste vide
if (! estVide()) {
premier= retirerPremiereOccurrenceRec(premier, v);
}
}
public ElementListe retirerPremiereOccurrenceRec(ElementListe l, int v) {
if (l== null) {
return l;
} else if (l.getValeur() == v) {
return l.getSuivant();
} else {
return new ElementListe(l.getValeur(),
retirerPremiereOccurrenceRec(l.getSuivant(), v));
}
}
Remarque : Linconvnient de cette mthode est quon cre des copies de toutes les cellules qui ne
contiennent pas llment recherch. Pour viter cela, la dfinition doit mlanger calcul et effets
de bord, comme suit :
public ElementListe retirerPremiereOccurrenceRec(ElementListe l, int v) {
if (l== null) {
return l;
} else if (l.getValeur() == v) {
return l.getSuivant();
} else {
l.setSuivant(retirerPremiereOccurrenceRec(l.getSuivant(), v));
return l;
}
}
1.4
Listes tries
Nous nous intressons maintenant aux listes chanes dans lesquelles les lments respectent un
ordre croissant. Dans ce cas, les mthodes de recherche, dinsertion et de suppression sont diffrentes.
Lordre des lments permet darrter plus tt la recherche dun lment qui nexiste pas dans la liste.
Aussi, linsertion ne se fait plus en dbut de liste, mais la place qui prserve lordre des lments.
Nous utiliserons une classe ListeTriee qui est trs similaire la classe Liste, mais dans
laquelle on sintresse uniquement aux mthodes de recherche (contientTriee), dinsertion
(insertionTriee) et de suppression (suppressionTriee). Les autres mthodes sont identiques celles de la classe Liste.
class ListeTriee{
ElementListe premier;
c
NFA031
CNAM
2012
11
1.4.1
La diffrence avec la mthode contient de Liste est que dans le parcours de la liste on peut
sarrter ds que la cellule courante contient un lment plus grand que celui recherch. Comme tous
les lments suivants vont en ordre croissant, ils seront galement plus grands que llment recherch.
Variante iterative :
public boolean contientTriee(int v) {
ElementListe elt = getPremier();
while (elt != null && elt.getValeur() < v) {
elt = elt.getSuivant();
}
return (elt != null && elt.getValeur() == v);
}
Variante rcursive :
public boolean contientTriee(int v) {
return contientTrieeRec(getPremier(), v);
}
private boolean contientTrieeRec(ElementListe elt, int v) {
if (elt == null) {
return false;
} else if (elt.getValeur() > v) {
return false;
} else if (elt.getValeur() == v) {
return true;
} else {
return contientTrieeRec(elt.getSuivant(), v);
}
}
1.4.2
Linsertion dun lment doit se faire la bonne place dans la liste. La bonne place est juste avant
la premire cellule qui contient un lment plus grand que celui insrer.
Variante iterative :
La figure 1.5 montre les deux cas dinsertion :
12
c
NFA031
CNAM
2012
en dbut de liste.
Elle est similaire alors lopration insertionDebut de la classe Liste
en milieu de liste.
La recherche de la position dinsertion se fait alors avec une mthode similaire celle utilise
pour la mthode suppressionPremier de la classe Liste, avec deux rfrences. Le prdcesseur doit tre modifi pour pointer vers la nouvelle cellule, tandis que celle-ci doit pointer
vers la cellule courante (ref).
pred
ref
liste
nouvelle liste
pred
ref
liste
e1
...
e1
...
ep
er
...
e
F IGURE 1.5 Insertion dans une liste trie
Dans la figure 1.5, droite, la rfrence ref peut ne pas pointer vers une cellule. Ceci arrive quand
llment insrer est plus grand que tous les lments de la liste. Dans ce cas, linsertion se fait
la fin de la liste, donc au moment de larrt de la recherche, ref est nulle et pred est sur la dernire
cellule de la liste. Ce cas est identique celui prsent dans la figure 1.5, la variable suivant de
la nouvelle cellule on donne tout simplement la valeur de ref.
public void ajouterTriee(int v) {
if (estVide()) {
premier = new ElementListe(v);
} else if (getPremier().getValeur() >= v) {
// Insertion au dbut de la liste.
premier = new ElementListe(v, premier);
} else {
// On veut chercher llment qui prcde notre valeur
// Le problme est que nous ne savons que nous avons
// llment prcdent que parce que le suivant
// est strictement plus grand que v.
ElementListe precedent = getPremier(); // Initialisation correcte car
// getPremier().getValeur() < v.
ElementListe elt = getPremier().getSuivant();
while (elt != null && elt.getValeur() < v) {
precedent = elt;
elt = elt.getSuivant();
}
precedent.setSuivant(new ElementListe(v, elt));
}
}
Variante rcursive :
Nous utilisons le mme schma de dcomposition rcursive de la liste, en un premier lment
(premier) et un reste de la liste (reste). Linsertion rcursive suit le schma suivant :
liste.insertionTriee(e) =
ListeTriee(e, liste)
c
NFA031
CNAM
2012
si e<=premier
13
1.4.3
La suppression de la premire occurrence dun lment dans une liste trie est assez similaire
la suppression dans une liste non trie. La seule chose qui change est la recherche de la cellule
supprimer, qui bnficie du tri des valeurs. Aussi, si plusieurs occurrences de llment existent, elles
sont forcment lune la suite de lautre cause du tri. Il serait donc plus simple dliminer toutes les
occurrences de llment que dans le cas des listes non tries. Nous nous limiterons cependant ici la
suppression de la premire occurrence seulement.
Variante iterative :
Par rapport la mthode suppressionPremier de la classe Liste, ici le parcours de la
liste la recherche de llment ne se fait que tant que ref pointe une valeur plus petite que celui-ci.
Une fois le parcours arrt, la seule diffrence est quil faut vrifier que ref pointe vraiment la valeur
recherche.
public void retirerPremiereOccurrence(int v) {
// On limine le problme de la liste vide
if (estVide())
return;
// Le but est de trouver llment qui prcde v...
// qui nexiste pas si v est la premire valeur=>
if (premier.getValeur() == v) {
premier = premier.getSuivant();
} else {
ElementListe precedent = null;
ElementListe elt = premier;
while (elt != null && elt.getValeur() < v) {
14
c
NFA031
CNAM
2012
Variante rcursive :
Par rapport la variante rcursive de la mthode suppressionPremier de la classe Liste,
une nouvelle condition darrt est rajoute :
quand llment liminer est plus petit que premier, il nexiste srement pas dans la liste et celle-ci
est retourne non modifie.
public void retirerPremiereOccurrence(int v) {
premier= retirerPremiereOccurrenceRec(premier, v);
}
private ElementListe retirerPremiereOccurrenceRec(ElementListe elt, int v) {
if (elt == null) {
return null;
} else if (v == elt.getValeur()) {
return elt.getSuivant();
} else if (v < elt.getValeur()) {
return elt;
} else {
elt.setSuivant(retirerPremiereOccurrenceRec(elt.getSuivant(),v));
return elt;
}
}
1.5
Considrons un exemple simple de programme qui manipule des listes chanes en utilisant la
classe Liste.
Le programme lit une suite de nombres entiers termine par la valeur 0 et construit une liste avec
ces entiers (sauf le 0 final). La liste doit garder les lments dans lordre dans lequel ils sont introduits.
Egalement, une valeur ne doit tre stocke quune seule fois dans la liste.
Le programme lit ensuite une autre suite de valeurs, galement termine par un 0, avec lesquelles
il construit une autre liste, sans se soucier de lordre des lments. Les lments de cette seconde liste
devront tre limins de la liste initiale.
A la fin, le programme affiche ce qui reste de la premire liste.
Remarque : Pour garder les lments dans lordre dintroduction, linsertion dun nouvel lment
doit se faire en fin de liste. La mthode ajouterAuDebut ne convient donc pas. On pourrait
se dbrouiller avec la mthode concatener, mais comme lopration est frquemment utile,
on suppose crite la mthode ajouterALaFin.
c
NFA031
CNAM
2012
15
16
c
NFA031
CNAM
2012