Informatique III: Programmation en C++ Listes haînées Lundi 9 Janvier 2006 1 2 Introdution Les listes hainées permettent de stoker un nombre d objets qui n a pas besoin d être spéifié a priori. Rajouter ou supprimer un objet dans une liste prend un temps onstant, la reherhe d un objet en revanhe prend un temps proportionnel à la taille de la liste. Une liste haînée est omposée de ellules qui haune ontient une eur et un pointeur vers une autre ellule. Par exemple pour une liste d entiers: 1 lass Cellule { 2 publi: 3 int ; 4 ; 5 }; 3 4
Voilà la onfiguration en mémoire pour une liste ontenant les eurs 9, 5, et 3 (ave la onvention que la dernière ellule a un pointeur nul). int: 5 int: 3 : 0 La lasse ListeEntiers elle-même aura la forme ante: 1 lass ListeEntiers { 2 ; 3 publi: 4 ListeEntiers(); 5 ~ListeEntiers(); 6 void ajouter(int n); 7 int taille(); 8 bool ontient(int n); 9 }; 5 6 1 ListeEntiers::ListeEntiers() { 2 = 0; 3 } 1 int ListeEntiers::taille() { 2 int n = 0; 3 for( = ;!= 0; = ->) n++; 4 return n; 5 } 6 7 bool ListeEntiers::ontient(int n) { 8 for( = ;!= 0; = ->) 9 if(-> == n) return true; 10 return false; 11 } 7 8
1 void ListeEntiers::ajouter(int n) { 2 = new Cellule; 3 -> = n; 4 -> = ; 5 = ; 6 } Ajouter (1) Configuration initiale 9 10 Ajouter (2) Ajouter (3) int: int: n 2 = new Cellule; 3 -> = n; 4 -> = ; 11 12
Ajouter (4) int: n 1 ListeEntiers::~ListeEntiers() { 2 while(!= 0) { 3 = ; 4 = ->; 5 delete ; 6 } 7 } 3 = ; 13 14 Destruteur (1) Destruteur (2) 2 ; 3 = ; 15 16
Destruteur (3) Destruteur (4) 4 = ->; 5 delete ; 17 18 Note: test de pointeur nul 1 int main(int arg, har **argv) { 2 ListeEntiers l; 3 l.ajouter(3); 4 l.ajouter(9); 5 l.ajouter(5); 6 out << l.taille() << endl; 7 } Le ompilateur peut onvertir automatiquement un pointeur en booléen. La règle est simplement que le pointeur nul orrespond à la eur false et toute autre eur de pointeur orrespond à true. Ainsi, pour faire une boule sur les ellules d une liste on peut par exemple érire diretement 1 for( = ; ; = ->) { } 19 20
Exerie: une liste omme une pile Une liste peut failement être utilisée omme une pile. Érivez une méthode bool ListeEntiers::vide() qui dit si la liste est vide et une méthode int ListeEntiers::pop() qui enlève la première ellule et renvoie la eur qu elle ontenait (et abort si la liste est vide). 1 bool ListeEntiers::vide() { 2 return == 0; 3 } 4 5 int ListeEntiers::pop() { 6 if(!= 0) { 7 p = ; 8 int result = p->; 9 = p->; 10 delete p; 11 return result; 12 } else abort(); 13 } 21 22 Modifier les éléments d une liste L utilisation de méthodes virtuelles permet de faire failement des opérations sur tous les éléments d une liste. Pour ela, ommençons par définir 1 lass FontionEntiere { 2 publi: 3 virtual int eur(int n) = 0; 4 }; Ensuite rajoutons: 1 void ListeEntiers::applique_fontion(FontionEntiere *f) { 2 for( = ;!= 0; = ->) 3 -> = f->eur(->); 4 } 5 6 void ListeEntiers::affihe() { 7 for( = ;!= 0; = ->) 8 out << -> << endl; 9 } 23 24
1 lass Multiplie : publi FontionEntiere { 2 int k; 3 publi: 4 Multiplie(int l) { k = l; } 5 int eur(int n) { return k * n; } 6 }; 1 int main(int arg, har **argv) { 2 ListeEntiers l; 3 l.ajouter(3); 4 l.ajouter(9); 5 l.ajouter(5); 6 FontionEntiere *m = new Multiplie(10); 7 l.applique_fontion(m); 8 l.affihe(); 9 } 25 26 Supprimer le premier élément Nous pouvons rajouter une méthode pour supprimer le premier élément. Il faut faire attention une fois de plus à ne jamais utiliser un objet qui a été détruit. 1 void ListeEntiers::supprimer_premier() { 2 ; 3 = ; 4 = ->; 5 delete ; 6 } Inverser une liste Si on veut retourner une liste, il suffit de parourir les ellules les unes aprés les autres et de faire pointer le hamp de haune sur elle qui préède. Une fois de plus, il faut faire attention à ne pas utiliser des pointeurs obsolètes. 1 void ListeEntiers::retourner() { 2 p = 0, * =, *s; 3 while(!= 0) { 4 s = ->; -> = p; 5 p = ; = s; 6 } 7 = p; 8 } 27 28
p s p s Configuration au début de la boule: p pointe sur la ellule préédente, et sur la ellule ourante. 2 s = ->; 29 30 p s p s 3 -> = p; 4 p = ; 31 32
Filtrer une liste Si l on veut éliminer d une liste toutes les eurs qui ne vérifient pas une ertaine propriété, on introduit une lasse qui test une propriété: p s 1 lass Indiateur { 2 publi: 3 virtual bool ok(int n) = 0; 4 }; 5 = s; Et on rajoute à la lasse ListeEntiers la méthode: 33 34 1 void ListeEntiers::filtre(Indiateur *ind) { 2 =, *, *pred = 0; 3 while(!= 0) { 4 = ->; 5 if(ind->ok(->)) pred = ; 6 else { 7 if(pred) pred-> = ; 8 else = ; 9 delete ; 10 } 11 = ; 12 } 13 } Exerie Comment utiliser e qui préède pour éliminer toutes les eurs qui ne sont pas multiples de k d une liste? lass Indiateur { publi: virtual bool ok(int n) = 0; }; lass ListeEntiers { ; publi: void filtre(indiateur *ind); 35 36
Exerie Corretion 1 lass EstMultiple : publi Indiateur { 2 int k; 3 publi: 4 EstMultiple(int n) { k = n; } 5 bool ok(int n) { return n%k == 0; } 6 }; Érire une méthode int omptage(indiateur *id) qui ompte ombien de eurs de la liste vérifient une ertaine propriété. lass Cellule { publi: int ; ; }; lass Indiateur { publi: virtual bool ok(int n) = 0; }; lass ListeEntiers { ; publi: 37 38 Corretion 1 int ListeEntiers::omptage(Indiateur *ind) { 2 int n = 0; 3 for( = ;!= 0; = ->) 4 if(ind->ok(->)) n++; 5 return n; 6 } 39