Liste Une liste est une séquence d’éléments. Concept important: Chaque élément possède une position dans la liste. Notation: De quelles opérations a-t-on besoin?
Opérations sur les listes Déterminer si un élément appartient à la liste Se déplacer dans la liste Déterminer la position courante Ajouter un élément à la position courante etc.
Concepts de base L’implémentation doit supporter le concept de position courante. Cela est fait en définissant la liste en termes de partition de doite et de gauche. Chacune de ces partitions peut être vide. Les partitions sont séparées par un séparateur (fence). L’élément courant est le premier élément de la partition de droite
Liste:Type de donnée abstrait template class List { public: virtual void clear() = 0; virtual bool insert(const Elem&) = 0; virtual bool append(const Elem&) = 0; virtual bool remove(Elem&) = 0; virtual void setStart() = 0; virtual void setEnd() = 0; virtual void prev() = 0; virtual void next() = 0;
Liste TDA (suite) virtual int leftLength() const = 0; virtual int rightLength() const = 0; virtual bool setPos(int pos) = 0; virtual bool getValue(Elem&) const = 0; virtual void print() const = 0; };
Liste: Exemples Liste: MaListe.insert(99); Résultat: Parcourir toute la liste: for ( MaListe.setStart(); MaListe.getValue(x); MaListe.next() ) Faire_quelque_chose(x);
Liste: fonction Find // Retourne vrai ssi K est dans la liste bool find(List & L, int K) { int x; for (L.setStart(); L.getValue(x); L.next()) if (K == x) return true; return false; }
Liste: Implémentation avec tableau
La classe Alist (1) template class AList : public List { private: int maxSize; // Taille max de la liste int listSize; // Nombre d’éléments int fence; // Position du séparateur Elem* listArray; // Tableau pour la liste public: AList(int size=DefaultListSize) { maxSize = size; listSize = fence = 0; listArray = new Elem[maxSize]; }
La classe Alist(2) ~AList() { delete [] listArray; } void clear() { delete [] listArray; listSize = fence = 0; listArray = new Elem[maxSize]; } void setStart() { fence = 0; } void setEnd() { fence = listSize; } void prev() { if (fence != 0) fence--; } void next() { if (fence <= listSize) fence++; } int leftLength() const { return fence; } int rightLength() const { return listSize - fence; }
La classe Alist(3) bool setPos(int pos) { if ((pos >= 0) && (pos <= listSize)) fence = pos; return (pos >= 0) && (pos <= listSize); } bool getValue(Elem& it) const { if (rightLength() == 0) return false; else { it = listArray[fence]; return true; } }
insert //Insère un élément au début de la //partition de droite template bool AList ::insert(const Elem& item) { if (listSize == maxSize) return false; for(int i=listSize; i>fence; i--) // Déplacer les éléments vers la droite listArray[i] = listArray[i-1]; listArray[fence] = item; listSize++; // Incrémente la taille return true; }
append //Ajoute un élément à la fin de la liste template bool AList ::append(const Elem& item){ if (listSize == maxSize) return false; listArray[listSize++] = item; return true; }
remove //Enlève et retourne le premier élément // de la partition de droite template bool AList ::remove(Elem& it) { if (rightLength() == 0) return false; it = listArray[fence]; for(int i=fence; i<listSize-1; i++) // Déplace les éléments vers la gauche listArray[i] = listArray[i+1]; listSize--; // Decrémente la taille return true; }
Implémentation d’une liste chaînée
La classe Link //Nœud pour liste simplement chaînée template class Link { public: Elem element; // Valeur associée au noeud Link *next; // Pointeur au nœud suivant Link(const Elem& elemval, Link* nextval =NULL) { element = elemval; next = nextval; } Link(Link* nextval =NULL) { next = nextval; } };
Position dans une liste chaînée(1)
Problème!
Position dans une liste chaînée(2)
La classe LList (1) // Implémentation d’une liste // simplement chaînée template class LList: public List { private: Link * head; // Pointe au début Link * tail; // Pointe à la fin Link * fence; // Sépareteur int leftcnt; // Nbre d’élém. à gauche int rightcnt; // Nbre d’élém. à droite void init() { // Initialisation fence = tail = head = new Link ; leftcnt = rightcnt = 0; }
La classe LList(2) void removeall() {// Libère l’espace mémoire while(head != NULL) { fence = head; head = head->next; delete fence; } public: LList(int size=DefaultListSize) { init(); } ~LList() { removeall(); } // Destructeur void clear() { removeall(); init(); }
La classe LList(3) void setStart() { fence = head; rightcnt += leftcnt; leftcnt = 0; } void setEnd() { fence = tail; leftcnt += rightcnt; rightcnt = 0; } void next() { //Ne fait rien s’il n’y a rien à droite if (fence != tail) { fence = fence->next; rightcnt--; leftcnt++; } }
La classe LList(4) int leftLength() const { return leftcnt; } int rightLength() const { return rightcnt; } bool getValue(Elem& it) const { if(rightLength() == 0) return false; it = fence->next->element; return true; }
Insertion
Insert/Append // Insère au début de la partition de droite template bool LList ::insert(const Elem& item){ fence->next = new Link (item, fence->next); if (tail == fence) tail = fence->next; rightcnt++; return true;} // Ajoute à la fin de la liste template bool LList ::append(const Elem& item){ tail = tail->next = new Link (item, NULL); rightcnt++; return true;}
Enlever un élément
Remove // Enlève et retourne le premier élément // de la partition de droite template bool LList ::remove(Elem& it) { if (fence->next == NULL) return false; it = fence->next->element; Link * ltemp = fence->next; fence->next = ltemp->next; if (tail == ltemp) tail = fence; delete ltemp; rightcnt--; return true; }
Prev // Se déplace au nœud précédent // Aucun changement s’il n’y a rien à gauche template void LList ::prev() { Link * temp = head; if (fence == head) return; //Rien à gauche while (temp->next!=fence) temp=temp->next; fence = temp; leftcnt--; rightcnt++; }
Setpos // Déplace le séparateur de sorte qu’il y // ait pos éléments à gauche template bool LList ::setPos(int pos) { if ((pos rightcnt+leftcnt)) return false; fence = head; for(int i=0; i<pos; i++) fence = fence->next; return true; }
Comparaison des implémentations Listes avec tableau: Insertion et retrait sont (n). Précédent et accès direct sont (1). Tout l’espace est alloué à l’avance. Pas d’espace autre que les valeurs. Listes chaînées: Insertion et retrait sont (1). Précédent et accès direct sont (n). L’espace augmente avec la liste. Chaque élément requiert de l’espace pour les pointeurs.
Comparaison de l’espace utilisé DE = n(P + E); n = DE P + E E: Space for data value. P: Space for pointer. D: Number of elements in array.
Liste doublement chaînée // Définition des nœuds template class Link { public: Elem element; Link *next; Link *prev; Link(const Elem& e, Link* prevp =NULL, Link* nextp =NULL) { element=e; prev=prevp; next=nextp; } Link(Link* prevp =NULL, Link* nextp =NULL) { prev = prevp; next = nextp; } };
Liste doublement chaînée
Insert
// Insérer au début de la partition // de droite template bool LList ::insert(const Elem& item) { fence->next = new Link (item, fence, fence->next); if (fence->next->next != NULL) fence->next->next->prev = fence->next; if (tail == fence) tail = fence->next; rightcnt++; return true; }
Remove
// Enlève l’élément courant // Retourne false en cas d’insuccès template bool LList ::remove(Elem& it) { if (fence->next == NULL) return false; it = fence->next->element; Link * ltemp = fence->next; if (ltemp->next != NULL) ltemp->next->prev = fence; else tail = fence; // Reset tail fence->next = ltemp->next; // Remove delete ltemp; // Libère l’espace rightcnt--; // Décrémente le compteur return true; }