Vous êtes sur la page 1sur 27

Structures de Données

Linéaires:
Les Listes

DEUXIEME PARTIE

Les listes simplement


chaînées

1
Listes chaînées

2
Chaînage simple

3
Chaînage double

4
Listes chaînées

◼ Une structure chaînée est une structure


dynamique formée de nœuds reliés
par des liens
◼ Première organisation :

Chaînage simple

tête

31 14 -5 17 100

tête

➔Liste vide

5
Chaînage simple

◼ Chaque élément est relié à son successeur par


un simple lien
◼ L’accès se fait de gauche à droite à partir de la
tête de liste
◼ La tête de liste est une pointeur sur le premier
nœud
◼ Si la valeur de la tête de liste est égale à null,
la liste est considérée comme vide
◼ Les opérations récupérer, supprimer, ajouter
nécessitent toutes un parcours séquentiel de
la liste: complexité=O(n)
◼ Le parcours est unidirectionnel

6
Implémentation
simplement chaînée
(en C)
◼ une liste simplement chaînée est un
pointeur sur une structure à deux
champs
◼ Un pointeur sur le nœud tête

◼ un entier indiquant le nombre

d’éléments dans la liste (lg) =taille


physique

typedef struct {
NOEUD tete;
int lg;
} laStruct,*LISTE;

7
Structure d’un nœud
info suivant

typedef struct structNoeud {


ELEMENT info;
struct structNoeud * suivant;
} structNoeud, * NOEUD;

◼ Un nœud est un objet récursif puisque suivant est


un pointeur sur un noeud
◼ Info est de type ELEMENT (qui peut être en
stockage direct ou indirect)
◼ NOEUD est un pointeur sur un nœud

8
Déclaration de la
structure
/********************************************************
* Fichier : LSTPTR.H
* Contenu : Déclaration de la structure de données choisie du TDA
LISTE par liste chainée avec en-tête.
* ici une liste est un pointeur sur la cellule en-tête
********************************************************/
#ifndef _LSTPTR_H
#define _LSTPTR_H
#include "ELTPRIM.h"

typedef struct structNoeud {


ELEMENT info;
struct structNoeud * suivant;
} structNoeud, * NOEUD;

typedef struct {
NOEUD tete;
int lg;
} laStruct,*LISTE;

#endif
9
Création et destruction
d’un noeud
NOEUD noeudCreer(ELEMENT e){

NOEUD n;

n =(NOEUD)malloc(sizeof(structNoeud));
if(!n)
printf ("\nPlus d'espace");
Else
{
elementAffecter(&n->info, e);
n->suivant = NULL;
}
return n;
}

void noeudDetruire(NOEUD n){


elementDetruire(n->info);
free(n);
}

10
estVide, estSaturée,
listeTaille
int estVide(LISTE L) {
return (L->lg == 0);
}

int estSaturee(LISTE L) {
NOEUD temp;
int saturee = 1; /* on suppose mémoire saturée */
temp = (NŒUD) malloc ( sizeof ( structNoeud ) );

if(temp != NULL) {
saturee = 0; /* mémoire non saturée */
free(temp); La saturation correspond à la
} saturation de la mémoire centrale
return saturee; c’est pourquoi on essaye de
réserver un nœud si ce n’est pas
} possible c’est qu’il y a saturation
sinon on le libère car on l’a réservé
juste pour le test

int listeTaille(LISTE L) {
return (L->lg);
}

11
Insertion (1)
Pos=4

20

2 8 14 -5 7

1 2 3 4 5

12
Insertion (2)

2 8 14 20 -5 7

p
13
Insertion (3)
p q
14 -5 nœud de rang pos
suivant n suivant
20

◼L’insertion d’un nouveau élément (de valeur) e au rang


pos consiste à :
◼ Créer un nouveau nœud n initialisé à la valeur e
◼ Si Insertion dans la tête (pos==1) :
◼ Relier le nœud n à la tête
◼ Modifier la tête
◼ Cas général (pos>1) :
◼ Relier le nœud de rang pos-1 (p) au nœud n
◼ Relier le nœud n au nœud de rand pos (q)

◼ Rq1: Si la liste est vide (lg==0) et (pos==1) c’est traité


dans le cas de pos==1
◼ Rq2: Si l’élément e est ajouté à la fin de la liste alors son

suivant = null : c’est traité dans le cas de pos>1


14
Insertion (4)
int inserer (LISTE L, ELEMENT e, int pos){
int succee=1;
int i;

NOEUD n, p, q; // c des pointeurs sur noeud c equivalent à:


// structNoeud * n

if (estSaturee(L)){
printf ("\nListe saturée");
succee=0;}
else {
if ((pos < 1) || (pos > L->lg + 1)) {
printf ("\nPosition invalide");
succee=0;
}
else {
n=noeudCreer (e);
/*on est sûr que la réservation va se faire car la mémoire
n'est pas saturée*/
if (pos == 1) /*insertion en tête de liste*/
{


n->suivant=L->tete;
L->tete = n;
}
15
Insertion (5)
else /*cas général (pos > 1) */
{
q= L->tete;
for (i=1; i<pos; i++) {
p = q;
q = q->suivant;
}
// q désigne l’élément de rang pos et p son prédécesseur
p->suivant=n;
n->suivant=q;
}
(L->lg)++;

}
}

return succee;
}

16
Suppression (1)
pos=4

2 8 14 -5 7

1 2 3 4 5

17
Suppression (2)
nœud de
rang pos

14 -5 7
p q
suivant

◼ Il faut distinguer les cas suivants:

◼ Suppression de la tête (pos == 1)


◼ modifier la tête de la liste

◼ Cas général (pos > 1)

◼ affecter au lien qui le désigne la valeur de son

lien suivant

◼ Rq1: Si la liste contient un seul élément (lg==1) et


(pos==1) c’est traité dans le cas de pos==1
◼ Rq2: Si suppression du dernier élément c’est traité dans
le cas pos>1
18
Suppression (3)
int supprimer (LISTE L, int pos ){
int i;
int succee=1;
NOEUD p, q;

if (estVide(L)) {
printf ("\nListe vide");
succee=0;
}
else {
if ((pos < 1) || (pos > L->lg))
{
printf ("\nPosition invalide");
succee=0;
}
else
{
q = L->tete;
/*suppression en tête de liste*/
if (pos == 1)


L->tete=L->tete->suivant;

19
Suppression (4)
else { /*cas général (pos > 1) */
for (i=1; i<pos; i++) {
p = q;
q = q->suivant;
}
p->suivant=q->suivant;
}
// q désigne l’élément de rang pos et p son prédécesseur

noeudDetruire(q);

(L->lg)--;
}
}
return succee;
}

Remarque: on utilise le même bloc de parcours que


dans l’insertion

20
Récupérer (1)
ELEMENT recuperer(LISTE L, int pos) {

/* s'il ya une erreur on affiche un message et on


retourne un element vide */

ELEMENT elt= elementCreer();


int i;
NOEUD p;

if (estVide(L))
printf ("\nListe vide");
else {
if ((pos < 1) || (pos > L->lg))
printf ("\nPosition invalide");
else {
p= L->tete;
for (i=1; i<pos; i++)
p = p->suivant;
elementAffecter(&elt,p->info);
}
}
return(elt);
}
21
Récupérer (2)

◼ s'il y a une erreur on affiche un


message et on retourne un élément
vide
◼ dans le cas de stockage indirect
récupérer retourne un pointeur sur
l'élément et non une copie de cet
élément cad si on modifie les champ
pointés par le résultat on modifie
directement la liste
◼ Exemple:
◼ elt=recuperer(L,2)
◼ elt->cin=0
◼ Le cin du client à la position 2 devient 0

22
Création d’une liste

LISTE listeCreer(void) {
LISTE L;
L=(LISTE)malloc(sizeof(laStruct));
if (! L)
printf("\nProblème de mémoire");
else {
L->lg = 0;
L->tete = NULL; /* initialiser la tête */
}
return(L);
}

23
Destruction d’une liste

void listeDetruire(LISTE L){


int i;
NOEUD p, q;
q= L->tete;
for(i = 1;i <= L->lg; i++)
{
p=q;
q=q->suivant;
noeudDetruire(p);
}
free(L);
}

24
Affichage

void listeAfficher(LISTE L){


int i=1;
NOEUD p;
p= L->tete;

for(i = 1;i <= L->lg; i++) {


elementAfficher(p->info);
p= p->suivant;
}
}

25
Copier

LISTE listeCopier(LISTE L){

LISTE LR = listeCreer();

int i;
ELEMENT elt;

for(i = 1;i <= L->lg; i++) {

elt=elementCreer();
elementCopier(&elt, recuperer(L,i));

inserer(LR,elt, i);
}
return LR;
}

Remarque: même code que dans l’implémentation


contiguë

26
Comparer

int listeComparer (LISTE L1,LISTE L2 )


{
int test= 1;
int i=1;

if (listeTaille(L1) != listeTaille(L2)) test= 0;

while ((i<=listeTaille(L1)) && (test)) {


if
(elementComparer(recuperer(L1,i),recuperer(L2,i))!=0)
test=0;
i++;
}

return test;
}

Remarque: même code que dans l’implémentation


contiguë
Notons que le résultat de comparaison dépend de
l’implémentation de elementComparer
27