Vous êtes sur la page 1sur 15

Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

CH3 : Les listes

1. Définition d’une liste :


Une liste est un ensemble fini d’éléments ordonnés, chaque élément contient un ou plusieurs champs.
Le premier élément de la liste est appelé « Tête ».
Le dernier élément est appelé « Queue »

Chaque élément possède un successeur sauf la queue (son successeur est la valeur NULL).

5 2 -25 7 ...... 2

Tête Eléme Queue


nt

La notation linéaire [5, 2, -25, 7, ..., 2] représentera la liste qui contient cette séquence.

Les structures de données linéaires induisent une notion de séquence entre les éléments (1er, 2ème, 3ème, suivant, dernier, …).

2. Operations sur les listes :


On distingue deux sortes d’opérations sur les listes :
• Les opérations qui concernent les éléments de la liste
• Les opérations concernant la liste elle-même.

2.1. Les opérations sur « les éléments » de la liste :


Insérer un élément (la 1ere place, en queue, au milieu)
Supprimer un élément (le 1er élément, le dernier élément, un élément au milieu)
Rechercher un élément afin d’afficher le contenue ou le modifier.

2.2. Operations globales sur la liste elle-même :

En plus des opérations vues précédemment, d’autres peuvent être s’effectuent sur la liste elle-même tels que :

Tester si la liste est vide.


Création d’une liste.
Vider une liste.
Calculer le nombre d’éléments dans une liste.
Trier une liste.
Fusionner deux listes : consiste à regrouper 2 listes en une seule.
Eclatement d’une liste : consiste a diviser la liste originale en 2 sous listes.
Etc...

3. Représentation des listes :


Il existe plusieurs méthodes pour implémenter des listes. Les plus courantes sont l’utilisation des tableaux (représentation
contiguë) et de pointeurs (représentation chainée).

3.1. La représentation contiguë (Tableau):


Les éléments de la liste sont simplement rangés dans le tableau à leur place respective séquentiellement (les uns à la suite
des autres).

M. bada 1
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

a) Inconvénients
L’utilisation de tableaux possède quelques inconvénients :
! PBM1 : La dimension d’un tableau doit être définie lors des déclarations et ne peut donc pas être
modifié
o Solution : définir un tableau dont la taille sera suffisante pour accueillir le plus grand nombre
d’éléments
" PBM2 : gaspillage d’espace mémoire au cas des petites listes.
" PBM3 : si la taille maximum venait à être augmentée # il faudrait modifier le
programme et le recompiler
! Lorsqu’on veut retirer (ou insérer) un élément du tableau « en début », il est nécessaire de décaler tous
les éléments situés après l’élément supprimé.

b) Avantages :

! L’accès au Keme element est immédiate : T[K]


! Trouver le suivant d’un élément : T[K+1]

3.2. La représentation chainée (utilisation des pointeurs) :

Une liste chaînée est une structure linéaire qui n'a pas de dimension fixée à sa création.

Ses éléments de même type sont dispersés dans la mémoire et reliés entre eux par des pointeurs.
• Un élément ou maillon d’une liste est toujours une structure (Objet composé) avec deux ou plusieurs
champs :
o Un ou plusieurs champ = Valeur : contenant l’information
o Un champ = Adresse : donnant l’adresse du prochain élément.

La liste est accessible uniquement par sa tête de liste c’est-à-dire son premier élément.

3.2.1. Structure d’un élément : Un élément de la liste est une structure de donnée définie comme suite :

Langage C Pascal (langage algorithmique)

struct element Type element = record


{
type1 Champ_1; Champ_1 : Type1 ;
.... ....
typeN Champ_N; Champ_N : TypeN ;

element* Suivant ; Suivant : ^element ;


} End ;

M. bada 2
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

Apres la déclaration de la structure élément, on peut créer des élément dynamiques en utilisant les pointeurs :

element* e ; Var e : ^element ; Var e : ↑element ;


e = (element*) malloc (sizeof(element)) ; .... ....
new (e) ; allouer (e) ;

Le résultat d’exécution de ce bloc d’instructions est le suivant :

(*e).champ1 (*e).suivant
ou ou
e->champ1 e->suivant
e
#100

Champ1 ... ChampN Suivant


*e

3.2.2. C’est quoi une liste exactement ? :

La déclaration d’un tableau T de N éléments en C mène à la création de N+1 variables : T[0], T[1], ...,T[N-1] et T.

# T : un pointeur (adresse) de la première case.

En quelque sorte, on peut dire que le tableau dans un programme n’est que l’adresse de la 1ere case = &T[0].

Comme pour les tableaux, une liste n’est que l’adresse du premier élément (Tête) de la liste.

a) Liste vide :
Une liste est dite vide si elle ne contient aucun élément. En d’autre terme, une liste vide est un pointeur qui
est égale à NULL.

Liste
Element* Liste ;
Liste =NULL ;
NULL

M. bada 3
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

4. Operations sur les listes chainées (langage C) :

4.1. Parcourir une liste :

Comme pour les tableaux, afin de parcourir une liste on a besoin de passer par touts les éléments en utilisant un index
(compteur ou position).
Pour afficher ou modifier le contenu d'un élément dans une liste chainée, l’index doit se pointer vers cet élément. Pour
cela il faut trouver un moyen qui permet de déplacer l’index d’un élément vers l’élément suivant.

Ex :
Un étudiant est définie par les champs suivant : N, Nom
Supposons qu’on a une liste d’étudiants déjà créée, dont la queue pointe vers null.

#100

3 ‘’aa’’ #50 6 ‘’...’’ #200 2 ‘’...’’ NULL

#100 #50 #200

a) Analogie avec les tableaux :

Tableau Liste chainée

Etudiant T [N] ; Etudiant* liste ;


Déclaration d’une
liste vide

Création .... Voir la section ....

Int i ; Etudiant* position ;


Déclaration d’un
compteur (index=
position)

i=0 ; Position = liste ;


Initialisation du
compteur

While (i<N) While (position != NULL)


Condition d’arrêt { {

Printf ("%d", T[i].N) ; Printf ("%d", (*position).N) ;


Afficher le numéro

i=i+1 ; Position = (*position).suivant ;


Incrémentation } }

I=N Position= NULL


Valeur du
compteur a la fin

M. bada 4
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

Etape1 : initialisation # position= Lise = #100;

Liste Position

#100 #100

3 ‘’aa’’ #50 6 ‘’...’’ #200 2 ‘’...’’ NULL

*position
#50 #200

(*position).suivant

Etape2 #la boucle while : 1ere incrémentation # position = (*position).suivant = #50;

Liste Position

#100 #50

3 ‘’aa’’ #50 6 ‘’...’’ #200 2 ‘’...’’ NULL

*position

#50 #200

(*position).suivant

Etape3 # la boucle while :2eme incrémentation # position = (*position).suivant = #200;

Position
Liste
#50
#100

3 ‘’aa’’ #50 6 ‘’...’’ #200 2 ‘’...’’ NULL

#100 #50 #200

M. bada 5
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

Etape4 # la boucle while : 3eme incrémentation (dernière) # position = (*position).suivant = NULL;

Position
Liste
NULL
#100

3 ‘’aa’’ #50 6 ‘’...’’ #200 2 ‘’...’’ NULL

#100 #50 #200

NB : A la fin de cette boucle le pointeur position = NULL.

4.2. Insérer un élément :

4.2.1. Insérer un élément à la fin de liste :


Insérer un élément à la fin de la liste consiste à modifier le champ « suivant » de la queue, et mettre à la place de la
valeur NULL l’adresse du nouvel élément que l’on veut insérer.

Liste

#100

3 ‘’aa’’ #50 6 ‘’...’’ #200 2 ‘’...’’ NULL

#100 #50 #200

#700

10 ‘’NV’’ NULL

Liste
Position
#100
#200

2 ‘’...’’ #700
3 ‘’aa’’ #50 6 ‘’...’’ #200

#100 #50 #200

e
#700

10 ‘’NV’’ NULL

M. bada 6
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

Pour insérer un élément à la fin il faut suivre les étapes suivantes :

Etape1 : création dynamique d’un nouvel élément (utilisation des pointeurs)

Etudiant* e ;
e= (Etudiant*) = malloc (sizeof (Etudiant) ) ;

Etape2 : initialisation de l’élément (lecture a partir d’un clavier) :

scanf ("%d", & (*e).N ) ;


scanf ("%s", & (*e).Nom) ;
(*e).suivant = NULL ;

Etape 3 : parcourir la liste jusqu'à le dernier élément (position -> pointe le dernier élément « la
Queue » et pas NULL !!! ) # donc il faut faire avancer le pointeur position jusqu'à que son suivant
= NULL.

Etudiant* position ;
position= liste ;
while ( (*position).suivant != NULL )
{
position = (*position).suivant ;
}

Etape 4 : établir la liaison entre la liste et le nouvel élément

(*position).suivant = e ;

4.2.2. Insertion en tête de la liste

Liste

#700

2 ‘’...’’ NULL
3 ‘’aa’’ #50 6 ‘’...’’ #200

2
#100 #50 #200

e 1
#700

10 ‘’NV’’ #100

*e

M. bada 7
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

Etape1 : création dynamique d’un nouvel élément (utilisation des pointeurs) + initialisation :

Etudiant* e ;
e= (Etudiant*) = malloc (sizeof (Etudiant) ) ;

scanf ("%d", & (*e).N ) ;


scanf ("%s", & (*e).Nom) ;

Etape 2 : établir la 1ere liaison N1 dans le schéma.

(*e).suivant = liste ;

Etape 3 : établir la 2eme liaison N2 dans le schéma.

Liste = e ;

4.2.3. Insertion au milieu :

Il existe plusieurs façons de savoir l’endroit d’insertion d’un élément :

Directe : dans ce cas on va utiliser directement une variable « place » qui donne la position où l’on doit
insérer le nouvel élément.
Indirecte : dans ce cas, il faut trouver un élément particulier « ex : l’étudiant Mohammed » à partir du
quel on déterminera la position du nouvel élément : (avant ou après).

Cas1 : insertion directe:


Pour insérer un élément à un endroit (K) de la liste, il est nécessaire d’abord de se positionner sur l’élément qui
vient juste avant le nouvel élément pour qu’on puisse établir la liaison entre la 1ere partie de la liste et le nouvel
élément.

Etape1 : création dynamique d’un nouvel élément (utilisation des pointeurs) + initialisation

Etudiant* e ;
e= (Etudiant*) = malloc (sizeof (Etudiant) ) ;

scanf ("%d", & (*e).N ) ;


scanf ("%s", & (*e).Nom) ;

Etape 2 : parcourir la liste jusqu'à trouver l’élément qui se trouve à la place : K -1 :

Etudiant* position ;
position= liste ;
int i=0 ;
while ( i < K-2 )
{
position = (*position). suivant ;
i=i+1 ;
}

M. bada 8
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

Etape 3: établir la liaison N3 : entre le nouvel élément et la 2eme partie de la liste.

(*e).suivant = (*position).suivant ;

Etape 4: établir la liaison N4 : entre la 1ere partie de la liste et le nouvel élément

(*position).suivant = e ;

2
Position

E1 E2 E3 E4 E5 NULL

Tête
4 3

Créer (Nv Element) Nv E


Suivant (Nv Element) =E4 1
Suivant (E3) = Nv Element

Remarque : attention d’inverser les étapes 3 et 4, car ceci mène a perdre la 2eme partie de la liste

4.3. Suppression d’un élément :

4.3.1. Supprimer le 1er élément :


Il y a deux actions, dans cet ordre, à réaliser :

• faire pointer la tête de liste sur le deuxième élément de la liste.


• libérer l'espace mémoire occupé par l'élément supprimé.

Il est nécessaire de déclarer un pointeur local (P1) qui va pointer sur l'élément à supprimer, et permettre de libérer
l'espace qu'il occupait.

P1

E1 E2 E3 E4 E5 NULL

Liste Etudiant* P1 ;
P1= liste ;
Liste = (*liste).suivant ;
Free (P1) ;

M. bada 9
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

4.3.2. Supprimer le dernier élément (utilisation de deux pointeurs) :

P2 P1

E1 E2 E3 E4 E5 NULL

Tête

Etudiant* p1 ; // utilisé pour pointer le dernier élément


Etudiant* p2 ; // utilisé pour pointer l’avant dernier élément

P1= liste ;
P2= liste ;

while ( (*P1).suivant != NULL )


{
P2=P1 ; // sauvegarder l’ancienne valeur de P1
P1= (*P1).suivant ; // avancer P1 seulement

}
// A la fin de cette boucle P1 est toujours avancé d’un pas par rapport a P2.

(*P2).suivant = NULL ;

free (P1) ; // libérer l'espace

4.3.3. Supprimer un élément au milieu :

P2 P1

E1 E2 E3 E4 E5 NULL

Tête

M. bada 10
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

I=0 ;
while ( i < K-1 )
{
P2=P1 ; // sauvegarder l’ancienne valeur de P1
P1= (*P1).suivant ; // avancer P1 seulement
i = i+1 ;
}
// A la fin de cette boucle : P2 pointe vers l’élément précèdent
// A la fin de cette boucle : P1 pointe vers l’élément suivant

(*P2).suivant = (*P1).suivant;

free (P1) ; // libérer l'espace

4.4. La création d’une liste chainée :


Pour créer une liste chaînée, il existe 2 méthodes :

4.4.1. création en pile : (LIFO)


Création d’une liste en insérant les nouveaux éléments au début de la liste.

Ex : si on veut introduire les 3 étudiants dont les prénoms sont : « Atef» puis « Hacene » puis « Seif»
Le résultat est : [seif, hacene, atef]

On remarque que le dernier élément saisi est le premier élément de la liste : (Last In First Out).

etudiant* Tete=NULL; // au début la liste est vide


etudiant* E;

int bol=1;// une variable utiliser pour arrêter la saisie

do {
//allocation d’espace mémoire pour le nouvel élément
E= (etudiant*) malloc(sizeof(etudiant)) ;

//lire le N et le Nom
scanf ("%i",&(*E).N);
scanf ("%s",&(*E).nom);

// Insérer le nouvel élément au début de la liste


(*E).suivant=Tete;
Tete=E;

//poser la question a l’utilisateur : est ce que vous voulez insérer un autre élément ?
//bol=0 # non , bol = 1 # oui

scanf ("%i",&bol);

}while (bol==1); // refaire a boucle si l'utilisateur tape sur 1(oui).

M. bada 11
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

4.4.2. création en file : (FIFO) :


Création d’une liste en insérant les nouveaux éléments à la fin de la liste.

Ex : si on veut introduire les 3 étudiants dont les prénoms sont : « Atef» puis « Hacene » puis « Seif»
Le résultat est : [atef, hacene, seif]

On remarque que le premier élément saisi est le premier élément de la liste : (First In First Out).

Etape 0 : initialisation

Tête = derElement
NULL = NULL

Etape 1 : boucle # i =1 # Tete= NULL (cette étape s’exécute une seule fois)

derElement

E1 NULL
1:
Création
Tête 2 d’élément

Etape 2 : boucle (i= 2 , 3 ......N) # ici : i=2 # 2eme élément

derElement =
derElement = @ E1 (*derElement).suivant ;
=@ E2
5

E1 @E2 E2 NULL
4

1:
Création
Tête
d’élément

NB : La variable ( derElement ) pointe toujours le dernier élément.

M. bada 12
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

Etape 2 : boucle (i= 2 , 3 ......N) # ici : i =3 # 3eme élément :

derElement =
derElement = @ E2 (*derElement).suivant ;
=@ E3
5

E1 @E2 E2 @E3 E3 NULL

4
1:
Création
Tête d’élément

NB : La variable ( derElement ) pointe toujours le dernier élément.

Les étapes sous forme d’instructions :

etudiant* Tete=NULL; // au début la liste est vide


etudiant* derElement //pointe tjr le dernier élément.
int i ;
for (i =1 ; i<=N ; i++)
{

//allocation d’espace mémoire pour le nouvel élément


E= (etudiant*) malloc(sizeof(etudiant)) ;
1:
//lire le N et le Nom Création
d’élément
scanf ("%i",&(*E).N);

scanf ("%s",&(*E).nom);
(*E).suivant = NULL ;

si (tete == NULL){
// Ce bloc sera exécuté une seule fois (le 1 er élément)
tete = E ; 2+3
derElement= Tete ;
}else{
// A partir du 2eme élément
(*derElement).suivant = E ;
4+5
derElement=(*derElement).suivant ;
}

M. bada 13
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

5. Types de listes :

Il existe différents types de listes chaînées :

5.1. Liste chaînée simple constituée d'éléments reliés entre eux par des pointeurs (suivant).

5.2. Liste chaînée ordonnée : c’est une liste simplement chainée où l'élément suivant est plus grand que le précédent.
L'insertion et la suppression d'élément se font de façon à ce que la liste reste triée.

5.3. Liste doublement chaînée : c’est une liste où chaque élément dispose non plus d'un seul pointeur mais de deux
pointeurs pointant respectivement sur l'élément précédent et l'élément suivant. Ceci permet de lire la liste dans les
deux sens, du premier vers le dernier élément ou inversement.

Structure d’un élément : liste


doublement chainée

struct element
{
element* Precedent ;
type1 Champ_1;
....
typeN Champ_N;

element* Suivant ;
}

Le pointeur Précèdent du premier élément ainsi que le pointeur Suivant du dernier élément
contiennent la valeur Nil.

5.3.1. Parcourir une liste doublement chainée :


a) De la tête vers la queue :
Il est possible de parcourir la liste doublement chaînée du premier élément vers le dernier. Le pointeur de
parcours, P, est initialisé avec l'adresse contenue dans Tête.
Il prend les valeurs successives des pointeurs Suivant de chaque élément de la liste. Le parcours s'arrête
lorsque le pointeur de parcours a la valeur NULL Cet algorithme est analogue à celui du parcours d'une liste
simplement chaînée.

M. bada 14
Chapitre 3 : les listes Université de Batna 2 Algorithmique et structures de données

b) De la queue vers la tête :


Il est possible de parcourir la liste doublement chaînée du dernier élément vers le premier. Le pointeur de
parcours, P, est initialisé avec l'adresse contenue dans Queue.
Il prend les valeurs successives des pointeurs Précédent de chaque élément de la liste. Le parcours s'arrête
lorsque le pointeur de parcours a la valeur NULL.

5.4. Liste circulaire : c’est une liste où le dernier élément pointe sur le premier élément de la liste.

5.5. Liste doublement chainée circulaire : il s'agit d'une liste doublement chaînée où : le dernier élément pointe le
1er élément et le premier élément pointe également sur le dernier.

Remarque : Ces différents types peuvent être mixés selon les besoins.

6. Inconvénients :
L’inconvénient des listes chainées réside dans l’accès séquentiel à leurs éléments.
En effet, pour accéder à un élément, il faut passer par tous les éléments qui le précèdent # augmentation du temps de calcul.

Afin de pallier ce problème, une autre structure de donnée peut etre utilisée, il s’agit de la structure ARBRE (voir chapitre 5).

M. bada 15

Vous aimerez peut-être aussi