Vous êtes sur la page 1sur 54

République Algérienne Démocratique et Populaire

Université des Sciences et de la Technologie Houari Boumediene


Faculté d’Electronique et d’Informatique
Département Informatique

Les Listes Chaînées


Les Listes Chaînées
Cours Algorithmique de 1ere Année MI
Présenté par : Dr. B. BESSAA
Un petit retour en arrière !
On a vu que lorsqu’un ensemble de variables ont les mêmes propriétés
et concernent un même objet (par exemple : les notes d’un étudiant),
on les regroupe dans une seule variable de type Tableau. Ce qui facilite
la manipulation des éléments de cet ensemble.
On a vu aussi, dans le cours des pointeurs, qu’à une variable de type
Tableau, on fait correspondre une seule adresse : c’est l’adresse de
base. Et à partir de cette adresse, on peut retrouver l’adresse (@) de
n’importe quel élément du tableau juste en ayant son indice (I), en
utilisant la fameuse formule : @ = N*(I-1)+@1
où @1 est l’adresse de base
Vous êtes d’accord ? Vous vous souvenez de ça ?
Oui, oui on vient juste de le voir.

Bien, alors comment cela est possible ?

2
Et bien ceci est possible, parce que les éléments du tableau sont stockés
en mémoire l’un à coté de l’autre, ils sont voisins, il sont adjacents. On
dit que les éléments du tableau sont contigus.
Au fait c’est une manière de représentation des données, on parle de la
représentation contigüe.
L’avantage principal de la représentation contigüe, est qu’à partir d’une
adresse de base, on peut accéder directement à un élément de
l’ensemble juste en ayant sa position (son indice). Ce qui permet
d’accélérer l’accès aux données.
Elémen
t s
E1 E2 E3 E4 E5 E6 E7 E8
1 2 3 4 5 6 7 8
es
Indic

Mais malheureusement, la représentation contigüe possède deux


inconvénients majeurs.
3
1- La taille de la structure de donnée
En effet, dans une représentation contigüe, on doit spécifier la taille de
la structure de donnée (la taille max du tableau). Et donc une fois le
tableau plein, on ne peut pas ajouter des éléments.
Mais on peut utiliser un tableau dynamique. Et dans ce cas on n’a pas besoin de
spécifier la taille max du tableau.
Bonne réflexion, oui, avec un tableau dynamique, on n’a pas besoin de
spécifier la taille dans la partie déclaration, mais au moment de
l’allocation dynamique (en exécution), il faut préciser la taille. Donc
même avec un tableau dynamique la taille est limitée.
Et quel est le deuxième inconvénient ?
Le deuxième est aussi important que le premier.

2- La mise à jour des données

4
2- La mise à jour des données
Alors si jamais on veut supprimer un élément ou insérer dans le mesure
du possible (si l’espace le permet), on doit faire toute une gymnastique
qui prend beaucoup de temps.
Oui on doit faire un décalage de tous les éléments.
Trèèèès bien, voici un exemple:
Soit un tableau de 10 éléments. Pour supprimer le deuxième élément,
on doit décaler tous les éléments à partir du troisième d’une case à
gauche (donc 8 décalages), ensuite on réduit la taille exacte (N) du
tableau
4 -1 3 8 1 -8 13 15 7 30

4 -1
3 83 81 1
-8 -8
13 13
15 15
7 7
30 30

3 8 1 -8 13 15 7 30
Alors ça c’est pour un tableau de 10, imaginez le travail à faire pour un
tableau de 1000 ???!!!! La même chose se fait pour l’insertion.
5
Donc que ça soit Statique ou Dynamique
La représentation Contigüe possède un avantage très intéressant
(l’accès rapide aux données), mais elle a un inconvénient majeur dans le
cas d’une mise à jour des données.

Cette manière de représentation est conseillée dans les problèmes où la


mise à jour est rare : conception d’un dictionnaire par exemple.
Par contre, elle déconseillée dans les cas où la mise à jour est fréquente.
Et alors, est-ce-qu’il y a solution à ce problème ?
Bien, quand on veut trouver des solutions à des problèmes pareils, il
faut viser les inconvénients et essayer de trouver des remèdes. Là on a
dit qu’il y a deux inconvénients. On va essayer de les éliminer un par un.
Commençant par le plus important: le deuxième.
Essayons donc de trouver une solution pour éviter les décalages pour
les opérations de suppressions ou d’insertions.
6
Le problème du décalage est dû au fait qu’il y a un ordre entre les
éléments qu’il faut respecter, c’est un lien implicite entre les éléments.
Alors si on veut éviter ce décalage, il faut casser cet ordre. Mais si on le
casse on perd le lien entre les éléments. La solution est de rendre ce
lien explicite.
Non, on n’a rien compris !!!
J’explique, soit cinq personnes alignés l’un après l’autre, et chacun
possède un numéro.
Quel le numéro du deuxième ?
C’est 19. 5 19 3 29 8
Bien, quel le numéro du
quatrième ?
C’est simple 29.
Donc vous avez pu répondre facilement car vous voyez l’ordre entre ces
personnes, il suffit de compter.
7
Alors maintenant je vais changer la situation, et je présente les mêmes
personnes de cette manière

5
29 3

Quel le numéro du troisième ? 8 19


Euuuuh, on ne sait pas !!!
Oui, et vous ne pouvez pas le savoir, parce qu’on a cassé l’ordre initial
entre les personnes qui était visuel (implicite).
Et pour que vous puissiez répondre à cette question, il vous faut cette
information. Mais comme elle n’est pas apparente, il faut la rendre claire
(explicite)
Et comment ?
On va voir.
8
Alors maintenant je vais demander à chacun de ces personnes de se
souvenir du numéro de son voisin gauche. Et moi je ne garde que le
numéro du premier, c’est le numéro 5.
Entre nous, je vous rappelle l’ordre initial, c’était : 5-19-3-29-8
Dans ce cas, la nouvelle situation sera:

5
29 3 19
8 29

8 19
fin 3
Je repose la question, quel est
le numéro du troisième ?
Alors, le premier c’est 5, son voisin le deuxième, c’est 19 ensuite son
voisin le troisième c’est le numéro 3.
Bien, vous voyez donc, maintenant le lien devient explicite, vous le
voyez.
9
En effet, dans la nouvelle situation, il existe un lien de chainage explicite
entre ces personnes:
5
29 3 3
19
8 29

8 19
fin 3

Oui, mais comment ça résout le problème du décalage ?


C’est simple, si on veut supprimer le deuxième par exemple, il suffit
de dire au premier que ton voisin n’est plus 19 mais le voisin de 19
qui est 3. Et donc on aura un nouveau chainage sans faire aucun
décalage.
On appelle cette représentation : La représentation Chainée
10
La Représentation Chainée d’un ensemble d’éléments ordonnés,
consiste à ajouter à chaque élément une information sur son suivant.
Donc chaque élément sera représenté par deux parties (Enregistrement)
1- Information : qui décrit l’élément lui-même.
2- Suivant : qui donne information (code) sur le suivant.
Et justement, les structures de données utilisant ce mode de
représentation, s’appellent:
Les Listes Chaînées
Il existe plusieurs types de listes chainées, linéaires et non linéaires
(LLSC, LLDC, LLCC, Arbres, Graphes,…), nous allons nous intéresser dans
ce cours aux (LLSC):
Listes Linéaires Simplement Chaînées
Une LLSC est une suite ordonnée d’éléments de (maillons) de même
type, reliés entre eux par un chainage simple (dans un seul sens).
Un maillon (élément) est un enregistrement composé de deux champs:
1- Information 2- Suivant
11
On peut toujours exploiter les tableaux (organisation contigüe) pour
représenter une LLSC (tableau d’enregistrements), dans ce cas le
suivant sera l’indice de l’élément suivant dans le tableau. Le dernier
élément aura l’indice 0 pour son suivant. La liste sera définie par l’indice
du premier élément.
Exemple:
Soit la liste suivante composée de 6 éléments, avec le premier élément
qui se trouve à l’indice 4:

Indices 1 2 3 4 5 6 7 8 9

Information 5.25 8.13 10.1 18.2 5.1 -1.8


7 0 5 1 2 3
Suivant

La représentation contigüe équivalente est:


18.2 5.25 -1.8 10.1 5.1 8.13
12
Mais dans ce cas on aura toujours le problème de la taille limitée du
tableau !!!
Effectivement, en plus son implémentation est un peu compliquée.
Donc cette représentation n’est pas intéressante, en passe alors à
l’élimination du deuxième inconvénient de la représentation contigüe
qui était justement la taille limitée du tableau.
Alors là c’est plus simple. Pensons au chainage qu’on a fait ?!
Les pointeurs ???
Exactement, on voit déjà dans le champ suivant du maillon de la liste, la
notion de l’adresse de l’élément suivant, donc un pointeur vers
l’élément suivant.
En utilisant les pointeurs, une liste sera définie par un pointeur sur le
premier maillon (élément), et le suivant sera lui aussi un pointeur vers
un maillon (élément suivant). Et donc, théoriquement, la taille ne sera
pas limitée.
13
Déclaration d’une liste (LLSC)
On a deux notations possibles pour déclarer une liste:
Notation 1:
Type
<NomMaillon> = Enregistrement
<IdInfo>:<TypeElt>;
<IdSuivant>: ^ <NomMaillon>;
Fin;
<TypeElt>: est le type de l’information qui peut être simple ou structuré.
Avec cette notation, les variables représentant des listes seront
déclarées par des pointeurs vers <NomMaillon>.
Exemple
Type Eliste = Enregistrement
Info : reel;
Suiv : ^ Eliste;
Fin;
Var L,Q : ^Eliste;
14
Notation 2:
Type <NomPtr> = ^ <NomMaillon>;
<NomMaillon> = Enregistrement
<IdInfo> : <TypeElt>;
<IdSuivant> : <NomPtr>;
Fin;
Avec cette notation, les variables représentant des listes seront
déclarées comme étant des <NomPtr>.
Exemple
Type Pliste = ^ Eliste;
Eliste = Enregistrement
Info : reel;
Suiv : Pliste;
Fin;
Var L,Q : Pliste;
On peut utiliser l’une des deux, mais personnellement je préfère la
deuxième, elle évite d’utiliser à chaque fois le symbole pointeur (^).
15
Enfin nous pouvons présenter un résumé sur la représentation des
données avec une comparaison des performances :

Représentation

Contigüe V Chainée
(Tableaux) S (Listes)

Mémoire Statique, Dynamique Dynamique, (Statique)

Taille Limitée Non Limitée

Accès Direct, Séquentiel Séquentiel

MAJ Complexe Simple

16
Manipulation Listes Chaînées
Le type liste n’est pas un type prédéfini, c’est l’utilisateur qui le définit
de sa manière, il faut juste respecter la syntaxe de déclaration.
La manipulation, dépend donc du problème à résoudre. Cependant il y a
des opérations standards qu’on peut retrouver dans différents
problèmes.
Nous allons voir principalement, la création des listes, l’insertion et la
suppression et quelques applications.
Une remarque importante lorsqu’on manipule une liste, il faut retenir
que le premier élément de la liste (Tête de liste) est le seul point d’accès
à la liste, et il ne faut surtout pas le perdre, car si on le perd, tous les
autres éléments seront perdus.
Si on perd la Tête on devient Fou
Pour tout ce qui suit, on va utiliser une liste d’entiers, mais le principe
des algorithmes reste le même pour tout autre type d’élément de la
liste.
17
Déclaration
Type Pliste = ^ Eliste;
Eliste = Enregistrement
Info : entier;
Suiv : Pliste;
Fin;
1- Création des listes chainées
Il existe deux modes de création d’une liste chainée:

a- LIFO (Last In First Out) : dans ce cas chaque élément ajouté est
placé en tête de liste, et donc le dernier élément ajouté sera le
premier de la liste.

b- FIFO (First In First Out) : dans ce cas chaque élément ajouté est
placé à la fin de la liste, et donc le premier élément ajouté sera le
premier de la liste.

18
Création LIFO (Last In First Out)
Pour créer une liste en mode LIFO, on utilise deux Pointeurs:
- Un pointeur pour la Tête de liste.
- Un pointeur intermédiaire pour créer les éléments.
L’opération passe par les étapes suivantes:
Soient T et P les deux pointeurs à utiliser.
1- Initialiser la tête T à Nil. T Nil ;
2- Créer un maillon avec P. Allouer(P);
3- Remplir la partie information. Lire(P^.Info);
4- Enchainer avec la tête T. P^.Suiv T;
5- Transmettre l’adresse de P à T. T P;
6- Aller à 2 pour créer l’élément suivant (boucle de création)
Exemple
Soit à créer une liste composée des éléments : 5 - 3 - 6
Donc le premier élément de la liste sera 6.
19
0- Var T,P : Pliste; Fin de la première itération:
1- T Nil ; T (Tête) pointe vers le premier élément @1 (5)
2- Allouer(P); Le suivant de 5 est Nil (donc c’est aussi le
3- Lire(P^.Info); dernier).
4- P^.Suiv T; P pointe aussi vers 5 ( ne dérange pas).
5- T P;
6- Aller à 2 pour créer l’élément suivant (boucle de création).
On va donc suivre ces étapes pour créer la liste : 5 - 3 - 6
Créer 2 espaces statiques ayant @T @1
Nil @P @1
pour adresses @T et @P
Créer un espace dynamique pour un
maillon (2 champs). Soit à l’adresse @1
@1, puis mettre @1 dans P (@P) 5
Lecture de 5 Nil
Transmettre Nil au champ Mettre P (@1) dans T
Suivant de l’adresse @1 T pointe vers @1
20
Deuxième Itération : Ajouter 3
0- Var T,P : Pliste;
1- T Nil ; Fin de la deuxième itération:
2- Allouer(P); T (Tête) pointe vers le deuxième élément @2 (3)
3- Lire(P^.Info); Le suivant de 3 est 5.
4- P^.Suiv T; P pointe aussi vers 3 ( ne dérange pas).
5- T P;
6- Aller à 2 pour créer l’élément suivant (boucle de création).
la liste à créer : 5 - 3 - 6
Créer 2eme maillon (@2) @1
@T @2 @P @2@1
@2
Nouveau lien pour P
Lecture de 3
Transmettre @1 au champ
@2 @1
Suivant de l’adresse @2 3 5
Chainage de @2 vers @1 @1 Nil
Mettre P (@2) dans T
T pointe vers @2
Nouveau chainage pour T 21
Deuxième Itération : Ajouter 6
0- Var T,P : Pliste;
1- T Nil ; Fin de l’opération
2- Allouer(P); T (Tête) pointe vers le troisième élément @3 (6)
3- Lire(P^.Info); Le suivant de 6 est 3, le suivant de 3 est 5.
4- P^.Suiv T; P pointe aussi vers 6 ( ne dérange pas).
5- T P;
6- Aller à 2 pour créer l’élément suivant (boucle de création).
la liste à créer : 5 - 3 - 6
Créer 3eme maillon (@3) @3
@T @2
@3 @P @2
Nouveau lien pour P
Lecture de 6
Transmettre @2 au @3 @2 @1
6 3 5
Suivant de l’adresse @3
Chainage de @3 vers @2 @2 @1 Nil
Mettre P (@3) dans T, T pointe vers @3; Nouveau chainage pour T
En ignorant le pointeur intermédiaire P, la liste de tête T sera : 22
Algorithme de création LIFO
L’algorithme permettant de créer un liste de N entiers peut donc s’écrire
Algorithme CreerLifo;
Var T,P : Pliste;
I,N : entier;
Debut
Lire(N); //nombre d’éléments de la liste
T ← Nil ; //initialiser à vide
Pour I ← 1 à N //boucle de création
Faire
Allouer(P);
Lire(P^.Info);
P^.Suiv ← T;
T ← P;
Fait;
Fin.
23
Algorithme de création LIFO
On peut transformer cet algorithme en procédure avec 2 paramètres
T et N, ils seront donc enlevés des variables locales.
Procedure CreerLifo(E/S/ T:Pliste ; E/ N : entier);
Var P : Pliste;
I : entier;
Debut
T ← Nil ; //initialiser à vide
Pour I ← 1 à N //boucle de création
Faire
Allouer(P);
Lire(P^.Info);
P^.Suiv ← T;
T ← P;
Fait;
Fin;

24
Création FIFO (First In First Out)
Pour créer une liste en FIFO, il faut d’abord créer la tête, ensuite créer
les autres éléments puis les enchainer à la tête, pour cela on utilise
trois Pointeurs:
- Un pointeur pour la Tête de liste (T).
- Un pointeur intermédiaire (P) pour créer les éléments.
- Un pointeur pour sauvegarder le dernier élément créé (Q).
L’opération passe par les étapes suivantes:
Soient T, P et Q les trois pointeurs à utiliser.
1- Initialiser la tête T à Nil.
2- Créer la tête T.
3- Initialiser Q avec T. (Q représente dernier élément créé : Queue)
4- Créer un maillon P.
5- Enchainer avec Q (ajouter P à la fin)
6- Affecter P à Q. (P devient le dernier élément)
7- Aller à 4 pour créer l’élément suivant. (boucle de création)
8- Affecter Nil au suivant de Q. (mettre fin à la liste)
25
Exemple
Soit à créer une liste composée des éléments : 5 - 3 - 6
Donc le premier élément de la liste sera 5.
Création du premier élément (Tête)
Créer 3 espaces statiques ayant
0- Var T,P,Q : Pliste;
pour adresses @T , @Q et @P
1- T Nil ;
2.1- Allouer(T); Créer un espace dynamique pour un
maillon (2 champs). Soit à l’adresse
2.2- Lire(T^.Info);
@1, puis mettre @1 dans T (@T)
3- Q T;
Lecture de 5

@T @1
Nil @Q @1 @P

Mettre T (@1) dans Q


@1 5 Q pointe vers @1 (dernier élément créé)
Fin de création de la Tête
T pointe vers le premier élément @1 (5).
Q pointe vers le même élément.
26
Créer les autres éléments (3 – 6)
Première itération : Ajouter 3
4.1- Allouer(P); Créer un espace dynamique pour un
4.2- Lire(P^.Info); maillon (2 champs). Soit à l’adresse
5- Q^.Suiv P; @2, puis mettre @2 dans P (@P)
6- Q P; Lecture de 3
7- Boucle Transmettre @2 au Suivant de Q (@1)
8- Q^.Suiv Nil; Chainage de @1 vers @2
Mettre P (@2) dans Q, Q pointe vers @2; Nouveau chainage pour Q

@T @1 @Q @1
@2 @P @2

@1 5 @2 3 Fin de la première itération:


T (Tête) pointe vers le premier élément
@2 @1 (5). Le suivant de 5 est 3.
P et Q pointent vers 3 ( ne dérange pas).
27
Créer les autres éléments (3 – 6)
Deuxième itération : Ajouter 6
4.1- Allouer(P); Créer un espace dynamique pour un maillon (2
4.2- Lire(P^.Info); champs). Soit à l’adresse @3, puis mettre @3
5- Q^.Suiv P; dans P (@P). Nouveau chainage pour P
6- Q P; Lecture de 6
Transmettre @3 au Suivant de Q (@2)
7- Boucle
Chainage de @2 vers @3
8- Q^.Suiv Nil;
Mettre P (@3) dans Q, Q pointe vers @3; Nouveau chainage pour Q

@T @1 @Q @2
@3 @P @2
@3

@1 5 @2 3 @3 6
@2 @3

28
Créer les autres éléments (3 – 6)
Deuxième itération : Ajouter 6
4.1- Allouer(P); Fin de la boucle, mettre le suivant de Q (@3) à Nil
4.2- Lire(P^.Info);
5- Q^.Suiv P; Fin de l’opération:
6- Q P; T (Tête) pointe vers le premier élément @1 (5).
7- Boucle Le suivant de 5 est 3, Le suivant de 3 est 6.
8- Q^.Suiv Nil; P et Q pointent vers 6 ( ne dérange pas).

@T @1 @Q @3 @P @3

@1 5 @2 3 @3 6
@2 @3 Nil

En ignorant les pointeurs intermédiaires P et Q, la liste de tête T sera :


29
Algorithme de création FIFO
L’algorithme permettant de créer un liste de N entiers peut donc s’écrire
Algorithme CreerFifo;
Var T,P,Q : Pliste;
I,N : entier;
Debut
Lire(N); //nombre d’éléments de la liste
T ← Nil ; //initialiser à vide
Si N ≠ 0 Alors
Allouer(T); Lire(T^.Info); // créer la tête
Q ← T; //dernier élément créé
Pour I ← 2 à N //boucle de création
Faire
Allouer(P); Lire(P^.Info);
Q^.Suiv ← P;
Q ← P;
Fait;
Q^.Suiv ← Nil; //mettre fin à la liste
Fsi;
Fin.
30
Algorithme de création FIFO
La même chose ici, on peut transformer cet algorithme en procédure
Procedure CreerFifo(E/S/ T : Pliste ; E/ N:entier);
Var P,Q : Pliste;
I : entier;
Debut
T ← Nil ; //initialiser à vide
Si N ≠ 0 Alors
Allouer(T); Lire(T^.Info); // créer la tête
Q ← T; //dernier élément créé
Pour I ← 2 à N //boucle de création
Faire
Allouer(P); Lire(P^.Info);
Q^.Suiv ← P;
Q ← P;
Fait;
Q^.Suiv ← Nil; //mettre fin à la liste
Fsi;
Fin.
31
Remarque
Pour simplifier le schéma représentant une liste, on peut négliger les
adresses, par exemple, la liste {8,5,-1,2} sera représentée par le schéma:
T

8 5 -1 2 Nil

Mais comment on peut savoir si la liste est FIFO ou LIFO ?

Bon, FIFO ou LIFO sont des modes de création, et non pas une
caractéristique de la liste. Donc, une fois créée, on ne cherche pas à
savoir comment elle a été créée. L’essentiel, on a une suite d’éléments
avec une Tête comme point d’accès.
Alors, on passe maintenant à l’insertion dans une liste. On suppose
qu’on à déjà une liste à laquelle on veut ajouter un élément.

32
2- Insertion dans une liste chainée
Soit T la tête d’une liste donnée, et X un élément (partie info) à insérer
dans cette liste.
Trois cas se présentent pour l’insertion:
1- Insérer au début.
2- Insérer à la fin.
3-Insérer au milieu.
Insérer au début ou à la fin, c’est clair, mais le milieu ?! Comment
on va le définir ?
Bien, quand on dit milieu, ce n’est pas dans le sens moitié, mais c’est
quelque part dans la liste, autre que le début et la fin.
Comment le définir ? Là c’est le cas d’utilisation qui le définit, ça peut
être une position (Ex: insérer à la 3 position), comme ça peut être avant
ou après une valeur donnée, ou encore avant ou après une adresse
donnée,...
Ce qui nous intéresse c’est le principe d’insertion au milieu,
indépendamment du cas de figure.
33
2.1- Insertion au début
Pour insérer au début, on utilise un pointeur intermédiaire (P) et on
passe par les étapes suivantes:
1- Créer l’élément contenant X. Allouer(P) ; P^.Info ← X;
2- Etablir un lien de chainage entre P et T. P^.Suiv ← T ;
3- Déplacer la Tête (T) vers P. T←P;
ATTENTION, l’ordre des actions est très important
Exemple: Soit la liste suivante {13,-5,2}, et on veut insérer X=7 au début
1-
2- @P @4 @T @4
@1
3-
@4 7 @1 13 @2 -5 @3 2
@1 @2 @3 Nil

En ignorant le pointeur intermédiaire P, la liste de tête T sera :


34
Procédure insertion au début
La procédure a 2 paramètres T et X. T est passé par référence (E/S), car
la tête va changer.
Procedure InsertDebut(E/S/ T:Pliste ; E/ X : entier);
Var P : Pliste;
Debut
Allouer(P);
P^.Info) ← X;
P^.Suiv ← T;
T← P;
Fin;
Et si la liste initiale est vide?

No problem !, même si elle est vide (T=Nil), ça marche, dans ce cas


l’action P^.Suiv ← T va affecter Nil au suivant, et on aura donc une liste
T avec un seul élément.
35
2.2- Insertion à la fin
Pour insérer à la fin, on utilise 2 pointeurs intermédiaires P et Q et on
passe par les étapes suivantes:
1- Parcourir la liste pour atteindre la fin (boucle avec Q)
2- Créer l’élément contenant X. Allouer(P) ;
P^.Info ← X; P^.Suiv ← Nil ;
3- Etablir un lien de chainage entre P et T. Q^.Suiv ← P ;
A la fin du parcours (action 1), Q pointe vers le dernier élément.
Exemple: Soit la liste suivante {13,-5,2}, et on veut insérer X=7 à la fin
1-
@T @1 @Q @3
@2
@1 @P @4
2-
3-
@1 13 @2 -5 @3 2 @4 7
@2 @3 @4
Nil Nil

En ignorant les pointeurs intermédiaires P et Q, la liste de tête T sera :


36
Procédure insertion à la fin
La procédure a 2 paramètres T et X. T est passé par référence (E/S), car
la tête va changer dans le cas où elle est initialement vide.
Procedure InsertFin(E/S/ T : Pliste ; E/ X : entier);
Var P,Q : Pliste;
Debut
Allouer(P);
P^.Info ← X;
P^.Suiv ← Nil;
Si T=Nil
Alors T ← P
Sinon Q ← T; //initialiser au début
Tantque Q^.Suiv ≠ Nil Faire Q ← Q^.Suiv ; Fait; // aller à la fin
Q^.Suiv ← P; //ajouter à la fin
Fsi;
Fin;
37
2.3- Insertion au milieu
L’insertion d’un élément X au milieu, consiste à insérer cet élément
après un élément pointé par un pointeur Q. Une fois qu’on trouve ce Q,
l’insertion passe par les étapes suivantes:
0- Recherche de Q.
1- Créer l’élément contenant X. Allouer(P) ; P^.Info ← X;
2- Etablir un lien entre P et le suivant de Q. P^.Suiv ← Q^.Suiv ;
3- Etablir un lien de chainage entre Q et P. Q^.Suiv ← P ;
Pour la recherche du pointeur Q qui précède le point d’insertion, on n’a
pas d’algorithme spécifique, car cette action dépend du problème posé.
Comme on l’a déjà signalé, ça peut être :
- Avant ou après une position.
- Avant ou après une valeur.
- Avant ou après une adresse.
- Vérifier une contrainte donnée, …
Donc on va s’intéresser aux actions d’insertions (1,2,3), ensuite on va
voir quelques exemple de recherche.
38
Exemple
Soit une partie de la liste {...,7,13,-5,2,…}, et on veut insérer X=9 entre
13 et -5.
Fin de l’opération, X=9
se trouve entre 13 et -5
@Q @5

@4 7 @5 13 @6 -5 @7 2
@5 @6
@x @7 @8

0- Recherche Q
1- Créer X
2- Lier P au Suiv de Q @x
3- Lier Q à P 9
@6

@P @x
39
Algorithme d’insertion au milieu
On va donner juste les actions d’insertion proprement dites, on
considère qu’on a le pointeur Q (après une recherche). Ensuite on va
donner un cas d’insertion avec un type de recherche.
Algorithme InsertMilieu;
----
Debut
---
// Actions de recherche, qui donne Q
Allouer(P);
P^.Info ← X;
P^.Suiv ← Q^.Suiv;
Q^.Suiv ← P;
---
Fin;

40
Exemple de recherches
Recherche de l’Adresse d’une Position
Etant donnée une position K, cette recherche renvoie l’adresse
(pointeur) de l’élément situé à cette position. Si la position n’existe pas,
elle revoie Nil.
Fonction AdressePos(E/ T : Pliste; E/ K : entier) : Pliste;
Var P : Pliste;
Debut
P ← T;
Tantque P ≠ Nil Et K>1
Faire P ← P^.Suiv; K ← K – 1; Fait;
AdressePos ← P;
Fin;
Utilisation
Si on veut insérer Après une position Y : Q ← AdressePos(T,Y);
Si on veut insérer Avant une position Y : Q ← AdressePos(T,Y-1);

41
Recherche de l’Adresse d’une Valeur
Etant donnée une valeur V, cette recherche renvoie l’adresse (pointeur)
de l’élément égale à cette valeur. Si la valeur V n’existe pas, elle revoie
Nil.
Fonction AdresseVal(E/ T : Pliste; E/ V : entier) : Pliste;
Var P : Pliste;
Debut
P ← T;
Tantque P ≠ Nil Et P^.Info ≠ V
Faire P ← P^.Suiv; Fait;
AdresseVal ← P;
Fin;
Utilisation
Si on veut insérer Après une valeur Val : Q ← AdresseVal(T,Val);

42
Recherche de l’Adresse du précédent d’une Valeur
Etant donnée une valeur V, cette recherche renvoie l’adresse (pointeur)
du précédent de l’élément égale à cette valeur. Si la valeur V n’existe
pas ou ne possède pas de précédent (Tête), elle revoie Nil.
Fonction AdrPreVal(E/ T : Pliste; E/ V : entier) : Pliste;
Var P,Q : Pliste;
Debut
AdrPreVal ← Nil; Q ← Nil;
Si T ≠ Nil Alors Si T^.Info ≠ V
Alors P ← T; Q ← T^.Suiv;
Tantque Q ≠ Nil Et Q^.Info ≠ V
Faire P ← Q ; Q ← Q^.Suiv; Fait
Fsi;
Si Q≠ Nil Alors AdrPreVal ← P Fsi;
Fsi;
Fin;
Utilisation
Si on veut insérer Avant une valeur Val : Q ← AdrPreVal(T,Val);
43
Exemple d’insertion regroupant différents cas
Soit T une liste d’entiers, écrire une procédure permettant d’insérer un
élément X à la position K.
Dans ce cas, on peut avoir les différents cas possibles (début, fin, milieu)
et même le cas d’erreur (impossible d’insérer : position n’existe pas)
Procedure Insert(E/S/ T : Pliste ; E/ X,K : entier ; S/ INS : booleen);
Var P,Q : Pliste;
Debut
Allouer(P); P^.Suiv ← X; INS ← Vrai; //créer l’élément
Si K<1
Alors INS ← Faux; //impossible d’insérer, pos n’existe pas
Sinon Si K = 1 Alors P^.Suiv ← T ; T ← P //insertion au début
Sinon Q ← AdressePos(T,K-1); //adresse du précédent de pos
Si Q = Nil
Alors INS ← Faux;
Sinon P^.Suiv ← Q^.Suiv ; Q^.Suiv ← P ; //insérer au milieu ou à la fin
Fsi;
Fsi;
Fsi;
Fsi;
Si Non INS Alors Liberer(P) Fsi; //pas d’insertion, annuler l’allocation mémoire
Fin;

44
Exemple d’insertion dans une liste triée
Soit T une liste d’entiers triée par ordre croissant, écrire une procédure
permettant d’insérer un élément X dans cette liste.
Dans ce cas, l’insertion doit respecter la contrainte de l’ordre des
éléments. L’insertion est toujours possible.
Procedure Insert(E/S/ T : Pliste ; E/ X : entier ; );
Var P,Q,C : Pliste;
Debut
Allouer(P); P^.Info ← X; //créer l’élément
Si T = Nil
Alors P^.Suiv ← T ; T ← P
Sinon Si X ≤ T^.Info
Alors P^.Suiv ← T ; T ← P //insertion au début
Sinon Q ← T; C ← T^.Suiv ; //chercher la position d’insertion
Tantque C ≠ Nil Et X > C^.Info Faire Q ← C ; C ← C^.Suiv ; Fait;
P^.Suiv ← Q^.Suiv ; Q^.Suiv ← P ; //insérer au milieu ou à la fin
Fsi;
Fsi;
Fin;

45
Récapitulation de l’insertion
L’insertion d’un élément X dans une liste de Tête T

Créer l’élément X Allouer(P); P^.Info ← X;

Insertion au Début Insertion à la Fin ≠ milieu


Insertion au Début

P^.Suiv ← T ; T ← P ; Chercher la Fin : Q Chercher Précédent


(Q^.Suiv = Nil) (Q )

P^.Suiv ← Q^.Suiv
Nil ; ; P^.Suiv ← Q^.Suiv ;
Q^.Suiv ← P ; Q^.Suiv ← P ;

IDENTIQUE
46
3- Suppression dans une liste chainée
Soit T la tête d’une liste donnée, et X un élément (partie info) à
supprimer de cette liste.
Comme pour l’insertion, là aussi trois cas se présentent :
1- Suppression au début.
2- Suppression à la fin.
3-Suppression au milieu.
Est-ce-que là aussi on peut regrouper la suppression à la fin et
la suppression au milieu ?

Oui, là aussi on peut le faire, et donc je ne vais traiter que deux cas :
début et autre que début
Ce qu’il faut retenir dans la suppression
Il faut toujours chercher le précédent de X (sauf le cas du début).
Comme pour l’insertion, la recherche dépend du cas à traiter.
A la fin de la recherche on à un pointeur Q vers le précédent.
47
3.1- Suppression au début
En général, la suppression consiste à détacher l’élément à supprimer en
mettant à jour le chainage, puis libérer l’espace réservé pour cet
élément.
Pour la suppression au début, on utilise un pointeur intermédiaire (P).
L’opération passe par trois étapes :
1- Sauvegarder le suivante de la tête dans P. P ← T^.Suiv ;
2- Libérer l’espace réservé pour la tête T. Liberer(T);
3- Relier la tête T à P. T←P;
Exemple: Soit la liste suivante {13,-5,2}, et on veut supprimer la tête (13)
1-
@T @2
@1 @P @2
2-
3-
@1 13 @2 -5 @3 2 Fin de l’opération
@2 @3 Nil 13 est supprimé
48
Procédure Suppression au début
La procédure a un seul paramètre T.
T est passé par référence (E/S), car la tête va changer.
Procedure SupprimeDebut(E/S/ T:Pliste);
Var P : Pliste;
Debut
Si T ≠ Nil Alors P← T^.Suiv ; Liberer(T); T ← P Fsi;
Fin;
Remarque
On peut aussi faire: P← T ; T ← T^.Suiv ; Liberer(P)
3.2- Suppression autre que le début
Comme on l’a fait pour l’insertion, on laisse le problème de la recherche
à coté, on considère juste le résultat de cette recherche qui donne un
pointeur Q sur le précédent de l’élément à supprimer.
Soit P le pointeur sur l’élément à supprimer. L’opération passe par deux
étapes seulement:
1- Mettre à jour le suivant de Q. Q^.Suiv ← P^.Suiv ;
2- Libérer l’espace réservé pour P. Liberer(P);
49
Exemple
Soit une partie de la liste {...,7,13,-5,2,…}, et on veut supprimer X=-5
Fin de l’opération
-5 est supprimé @Q @5 @P @6

@4 7 @5 13 @6 -5 @7 2
@5 @6
@7 @7 @8

0- Recherche Q , P
1- Mettre à jour le suivant de Q
2- Libérer l’espace de P
Donc dans le cas d’un algorithme, une fois qu’on à le P et le Q, les deux
actions de suppression seront:
Q^.Suiv ← P^.Suiv ;
Liberer(P);

50
Exemple de suppression regroupant différents cas
Soit T une liste d’entiers, écrire une procédure permettant de supprimer un
élément à la position K.
Dans ce cas, on peut avoir les différents cas possibles (début, fin, milieu) et même
le cas d’erreur (impossible de supprimer : position n’existe pas)
Procedure Supprime(E/S/ T : Pliste ; E/ K : entier ; S/ SUP : booleen);
Var P,Q : Pliste;
Debut
SUP ← Vrai;
Si T ≠ Nil
Alors SUP ← Vrai;
Si K<1
Alors SUP ← Faux; //impossible de supprimer, pos n’existe pas
Sinon Si K = 1 Alors P← T ^.Suiv ; Liberer(T); T ← P //suppression au début
Sinon Q ← AdressePos(T,K-1); //adresse du précédent de pos
Si Q = Nil Alors SUP ← Faux;
Sinon P ← Q^.Suiv ;
Q^.Suiv ← P^.Suiv ; Liberer(P) //milieu ou à la fin
Fsi;
Fsi
Fsi;
Fsi;
Fin;
51
Quelques Applications courantes
Affichage des élément d’une liste
Procedure Affichage(E/ T : Pliste );
Var P : Pliste;
Debut
P←T;
Tantque P ≠ Nil
Faire Ecrire(P^.Info); P ← P^.Suiv ; Fait;
Fin;
Détruire une liste (supprimer tous les éléments)
Procedure Detruire(E/ T : Pliste );
Var P : Pliste;
Debut
Tantque T ≠ Nil
Faire P ← T ; T ← T^.Suiv ; Liberer(P); Fait;
Fin;
52
Trier une liste
Procedure Tri(E/ T : Pliste );
Var P,Q : Pliste; X: entier;
Debut
P←T;
Tantque P^.Suiv ≠ Nil
Faire Q ← P^.Suiv ;
Tantque Q ≠ Nil
Faire Si Q^.Info < P^.Info
Alors X ← P^.Info ; P^.Info ← Q^.Info ; Q^.Info ← X;
Fsi;
Q ← Q^.Suiv ;
Fait;
P ← P^.Suiv ;
Fait;
Fin;

53
brbessaa@gmail.com

54

Vous aimerez peut-être aussi