Listes chaînées simples
Définition
La liste chaînée organise l’information séquentiellement comme dans la structure de
tableau. Chaque maillon/nœud/cellule de la chaîne contient une information et un lien sur le
nœud suivant.
On représentera la liste de la façon suivante :
L’avantage de
8 4 2 1 cette structure par
rapport à la structure
tableau est qu’elle est
deb
dynamique, on peut
8 4 2 1 Null ajouter et retirer des
éléments de la liste à
notre guise, en ayant
une Liste simplement chaînée gestion mémoire
optimale. Il n’est donc
pas nécessaire de connaître à l’avance la taille maximale de la liste.
Mise en oeuvre
Une liste chaînée est une suite de pointeurs sur des objets contenant l’information. La
structure de base d’une liste chaînée est la suivante :
Voici par exemple des éléments, qui ont pour vocation
• de contenir une valeur entière ;
• d'être chainé avec un autre élément.
struct maillon {
int val;
struct maillon *suiv;
};
1 Notions de base : les listes simples
Pour représenter une liste, il nous faut un pointeur sur le premier
élement. Ce pointeur qui sera nul si la liste est vide.
1
struct maillon * deb;
2. Parcours d’une liste (pour effectuer un traitement sur les éléments de la liste).
Nous aurons un pointeur qui contiendra d'abord l'adresse du premier élément (soit deb), puis
du second, etc.
struct maillon *ptr = deb; // adresse du premier (si il existe)
while (ptr != NULL) {
printf("%d\n", ptr->val);
ptr = ptr->suiv; // passage au suivant
}
Ou bien avec la boucle for
for (struct maillon *ptr = deb; ptr != NULL; ptr = ptr->suiv) {
printf("%d\n", ptr->val);
}
3 Pour construire une chaine vide
Au départ, la chaine est vide, le pointeur de début est donc initialisé à NULL.
deb = NULL;
4 Insertion en tête de liste.
Si elle est vide, il faut mettre dans deb l'adresse d'un nouvel Element, créé par malloc,
contenant la valeur souhaitée et qui n'a pas de suivant. Et dire que cet élement est maintenant
le premier de la liste. Soit un code de la forme :
deb
36
if (deb == NULL) {
struct maillon *temp= (struct maillon *)malloc(sizeof (struct maillon));
temp->val = 36;
temp->suiv = NULL;
deb = temp;
}
2. Si la liste n'est pas vide, c'est presque pareil, sauf que le suivant du nouvel Element est l'ancien premier
(qui devient donc le deuxième).
if (deb != NULL) {
struct maillon *temp= malloc(sizeof (struct maillon));
temp->val = 36;
temp->suiv = deb;
deb= temp;
2
}
3. Mais on remarque que le premier cas est "couvert" par le second : si deb est NULL, affecter la
valeur NULL ou celle de deb, c'est la même chose. Donc on n'a pas besoin de tester deb pour séparer
les cas, et on se contente de
// allocation d'un nouvel élément
struct maillon *temp= (struct maillon *)malloc(sizeof (struct maillon)); // 1
// remplissage de l'élément
temp->val = 6;
temp->suiv = deb; // 2
// mise à jour du pointeur de début de liste
deb = temp; // 3
Ci dessous une illustration :
deb
temp 1 2
6
2
5 Insertion en fin de liste.
deb
6
6. Insertion après un élément dans la liste.
deb
3
7. Suppression du premier élément
il faudra libérer le premier élement et mettre à jour le pointeur de début de liste. Mais
attention à procéder dans le bon ordre
struct maillon old = deb;
deb = old->suiv;
free(old);
deb
2 1 8
old
( à libérer)
8. Suppression d’un élément dans la liste
deb
8 4 2 1
9. Suppression d’un élément en fin de la liste élément
deb
6
à libérer
Pour bien comprendre, prenez le temps de faire un dessin avec toutes les étapes.
4
Déplacement
10. Déplacement d’un élément en fin de la liste élément
3 8 4 2 1
3 8 4 2 1
Listes
doublement chaînées
Définition
3 2 8 4 1
Déplacement d’un nœud dans la liste
L’inconvénient des listes simplement chaînées est qu’il n’est possible de les parcourir que dans
un seul sens. Pour qu’il soit possible de les parcourir dans les 2 sens il faut déclarer 2 liens dans la
structure de liste : un pour le nœud suivant et un pour le nœud précédent. C’est ce qu’on appelle une
« liste doublement chaînée ».
La liste utilisée précédemment aurait la représentation graphique suivante :
Mise en œuvre
La liste doublement chaînée
demande un lien supplémentaire par nœud par
8 4 2 1
rapport à la liste simplement chaînée :
La liste doublement chaînée
demande un lien supplémentaire par nœud par
rapport à la liste Liste doublement chaînée simplement chaînée :
En programmation procédurale, il faut utiliser les pointeurs explicite:
Structure Noeud
info
Nœud *Nœud_suivant
Nœud *Nœud_précédent
Par la suite on désignera le nœud suivant par suivant(nœud) et le nœud précédent par
précédent(nœud).
5
La déclaration d’une liste doublement chaînée se fera de la manière suivante :
Début ListeDouble
Variables :
Nœud tete
tete NIL
Fin ListeDouble
Tous les algorithmes qui suivront sont des fonctions qui doivent être contenues dans
l’algorithme précédent.
Primitives
Déplacement
Le déplacement d’un nœud dans l’arbre nécessite donc la modification de 6 liens (le double
par rapport à une liste simplement chaînée).
Début
3 8 4 2 1
3 8 4 2 1
3 2 8 4 1
Déplacement d’un nœud dans la liste doublement chaînée
Modification(tete,nœud_depart,nœud_fin) // on suppose que nœud_depart est le nœud qu’on veut déplacer,
et nœud_fin, le nœud derrière lequel on veut mettre le nœud choisi. Si nœud_fin est vide on déplace le nœud devant
la tete de liste
Variables locales :
Nœud nœud_choisi
nœud_choisi nœud_depart
suivant(precedent(nœud_depart)) suivant(nœud_choisi) // si precedent ≠ NIL
6
precedent(suivant(nœud_depart) precedent(nœud_choisi) // si suivant≠ NIL
Si nœud_fin est vide Alors
suivant(nœud_choisi) tete
precedent(nœud_choisi) NIL
precedent(tete) nœud_choisi
tete nœud_choisi
Sinon
suivant(noeud_choisi) suivant(nœud_fin)
suivant(nœud_fin) nœud_choisi
precedent(nœud_choisi) nœud_fin
precedent(suivant(nœud_choisi)) nœud_choisi
FinSi
Fin Modification
Suppression
On l’aura compris, les différentes primitives pour une liste doublement chaînée demande 2
fois plus de modification sur les liens. Il faut donc modifier 2 liens pour supprimer un élément dans une
liste doublement chaînée.
8 4 2 1
Début
8 4 2 1
8 4 1
Suppression d’un nœud dans la liste doublement chaînée
Suppression(tete,nœud) // on suppose que nœud est le nœud à supprimer
precedent(suivant(nœud)) precedent(nœud) //si suivant ≠ NIL
suivant(precedent(nœud)) suivant(nœud) // si precedent ≠ NIL
Liberer(nœud)
7
Fin Suppression
Insertion
Pour l’insertion il faut modifier 4 liens :
Début 8 4 2 1
8 4 2 1
8 4 2 9 1
Insertion d’un nœud dans la liste doublement chaînée
Insertion(tete,nœud,nœudAInsérer) // on suppose que nœud est le nœud derriere lequel on veut insérer
noeudAInsérer
Si nœud est vide Alors
suivant(noeudAInserer) tete
precedent(noeudAInserer)NIL
precedent(tete) noeudAInserer
tete noeudAInserer
Sinon
precedent(noeudAInserer) nœud
suivant(nœudAInserer) suivant(nœud)
suivant(nœud) noeudAInsérer
precedent(suivant(noeudAInserer)) noeudAInserer // si suivant ≠ NIL
FinSi
Fin Inse