Vous êtes sur la page 1sur 35

3 LES LISTES CHAINEES

3.1 Introduction

Le choix de la structure de donnée pour le chargement d’une suite de valeur se heurte à


des problèmes au niveau programmation.
Supposons que nous voulons charger une suite de valeurs en mémoire pour effectuer
quelques opérations.
Entrée:
V1
V2
..
Vn
Quelle structure de donnée pourrait convenir ?

Tableau:
C’est la première structure qui peut satisfaire ce problème. Il suffit dans ce cas de déclarer
un tableau avec une taille fixe (obligés..).
Exemple:
int t[1000];
•Avantage: simple à utiliser
•Limite : -Allocation statique donc soit gaspillage de l’E.M ou sous-dimensionnement
- Le tableau ne peut pas dépasser la taille d’un segment memo

Gestion de la mémoire (Segmentation)


N:taille d’un segment

 La taille du tableau ne peut pas dépasser N octets


 Allocation dynamique en bloc
- Cette solution va permettre d’allouer à l’exécution le juste nécessaire.
int *t, n;
scanf(« %d »,&n);
t=(int *)malloc(n*sizeof(int));
• Avantage: Elle permet d’éviter l’inconvénient de
l’allocation statique qui exige une taille fixe.
•Limite : C’est une allocation en bloc, à l’exécution on
peux trouver des portions libres de taille
globale satisfaisantes mais pas un bloc
d’octets consécutifs.
Allocation morcelée
Au lieu d’allouer de manière dynamique un seul bloc de cases consécutives,
on va procéder à une allocation unitaire. Pour chaque élément on va effectuer
une allocation spécifique.
V1
V2
V3
V4
..
Vn

Avantage: L’allocation est faite avec des cellules de


petite taille, on a plus de chance de trouver un segment mémoire
Qui contient un espace suffisant pour contenir une valeur
Problème: Comment parcourir les différents emplacements alloués ?
Solution : Le chainage
@1 @2 @3 @n
V1 @2 V2 @3 V3 @4 V4 NUL

Noter que nous avons besoin de l’adresse du premier élement pour que l’on puisse faire le
parcours des différentes éléments.

Cette structure est appelée liste chainée.

3.2 Type associé à une liste


Une liste chainée contient donc 2 informations:
• La partie donnée de chaque élément, son type dépend des données que nous allons
charger.
•La partie chainage qui est un pointeur sur l’élément suivant
Nous pouvons définir le type d’une liste comme suit:
Strcut liste
{
DATA d;
struct liste *next;
};
typedef struct liste LISTE;
d next d next d next d next

Le premier élement de la liste on l’appelle first.

3.3 Opérations sur les listes chainées.

3.3.1 Création (load), insertion , affichage et libération de la liste


Le programme ci-dessous implémente un exemple d’une liste chainée d’étudiants.
On y trouve:
• La déclaration du type de la liste
• Une fonction qui charge une liste chainée à partir d’une lecture de l’entrée standard
• Une fonction d’affichage du contenu de la liste
• Une fonction de libération de l’espace mémoire de toutes les allocution faites
Appel de fonction et argument pointeur passé par adresse:

La fonction load a passé first par adresse pour la fonction insere_element


first_l
LISTE *load()
{
LISTE *first; *first_e

//insere_element(first, d); Probleme, first ne peut pas être modifié

insere_element(&first,d); first qui est un pointeur est appelé par adresse


}

first _e
&first _l

void Insere_element(LISTE **first, DATA d) //first est un pointeur sur un pointeur


{
//double pointeur
}
Insertion en tete
p first

d first
p->d p->next

nouveau first

Void insert_element(LISTE **first,DATA d)


{
p=(LISTE*)malloc(sizeof(LISTE);
p->d=d;
p->next=*first;
*first=p;
}
Libérer la liste

Libérer l’espace alloué pour chaque élément de la liste

first

p
p
p

for(p=first;p;)
{
t=p->next;
free(p);
p=t;
}
3.3.2 Chercher un élément dans une liste:

Il s’agit d’écrire une fonction qui retourne l’adresse de l’élément d’une liste qui vérifi
un critère.
On va supposer que dans la partie DATA il y a un champs qui s’appelle key, et on va
chercher dans la liste cet élément .
struct data
LISTE * seek_element(LISTE *first, int key) {
{ int key;
LISTE *p; …};typedef strcut data DATA;
for(p=first; p ; p=p->next) //for(p=first; p!=NULL ; p=p->next)
{
if(p->d.key==key)
return(p);
}
d next NULL
return(NULL);
} 5
3.3.3 pointer sur le précédent d’un élément p

preced

LISTE *seek_preced(LISTE *first,LISTE *p)


{
LISTE *t;
for(t=first;t;t=t->next)
if(t->next==p) return(t);
return(NULL);
}
3.3.4 Afficher les éléments d’une liste

void view(LISTE *first) //version 1


{
LISTE *p;

for(p=first;p;p=p->next)
print_data(p->d);
}

void view(LISTE *first) //version 2


{
for(;first;first=first->next)
print_data(first->d);
}
3.3.5 Suppression de la tete de la liste

First->next

first (nouveau)

Void delete_first(LISTE **first)


{
LISTE *p;
if(*first)
{
p=*first;
*first=*first->next;
free(p);
}
}
3.3.6 Suppression d’un élément d’une clé donnée

P
preced P->next
void delete(LISTE **first,int key)
{
LISTE *p;
if( (p=seek_element(*first,key))
{
if(p==*first)
*first=*first->next;
else
{
preced=seek_preced(*first,p);
preced->next=p->next;
}
free(p);
}
}
3.3.7 Récupérer le dernier élement d’une liste

p p

LISTE *seek_last(LISTE *first)


{
LISTE *p;
for (p=first; p && p->next; p=p->next);
;
return(p);
}
3.3.8 Supprimer le dernier éléments d’une liste

void delete_last(LISTE **first)


{
LIST *last;

last=seek_last(*first);
if(last)
{
preced=get_prec(*first,last);
if(last==*first)
*first=NULL;
else
preced->next=NULL;
free(last);
}
}
3.3.9 Insertion en fin de liste

void insert_last( LISTE **first,DATA d)


{
LISTE *p;

p=(LISTE*)malloc(sizeof(LISTE));
p->d=d;
p->next=NULL;
last=get_last(*first);
if(!*first)
*first=p;
else
last->next=p;
}
3.3.10 Afficher une liste à l’envers

void view_inverse(LISTE *first)


{
LISTE *p;

for(p=get_last(first); p ; p=seek_preced(first,p) )
print_data(p->d);

}
3.4 Les fonctions récursives

3.4.1 Définition
Une fonction récursive est une fonction qui fait appel à elle-même.
Forme d’une fonction récursive:

void f(int p)
{

….
f(p-1);
}

La récursion doit être conditionnée par une condition dite condittion de récursivité
void f(int p)
{

….

if(p)
f(p-1);
}
3.4.2 Exemple

Ecrire une fonction qui retourne le factoriel d’une valeur n


Version itérative: version récursive

fact(n)= 1x2x3 .. x(n-1)xn fact(n)=fact(n-1)*n si n>1


fact(0)=1;

int fact(int n) int fact(int n)


{ {
int i,f; if(n)
for(f=1,i=2;i<=n;i++) return(fact(n-1)*n);
f=f*i; return(1);
return(f); }
}
Trace à l’exécution:

Exemple n=3
fact( int n) //n=3
{
return(fact(n-1)*n); fact( int n) //n=2
{ fact( int n) //n=1
} return(fact(n-1)*n); {
Pile return(fact(n-1)*
3 2 } n)*n);
fact( int n) n=0
Sauvegarde du contexte } {
return(1);

}
3.5 Fonction récursives et listes chainées.
3.5.1 Afficher le contenu d’une liste

Afficher une liste chainée.

first first->next

[ ]
Afficher la liste first c’est : afficher l’élément first et afficher ensuite la liste first-> next

void view(LISTE *first)


{
if(first)
{
print_data(first_>d);
view(first->next);
}
}
3.5.2 Afficher à l’envers le contenu d’une liste

void view_inverse(LISTE *first)


{
}
3.5.3 Compter le nombre d’élements d’une liste

int nb_element(LISTE *first)


{
}
3.5.3 Chercher un élement dans la liste

LISTE * seek_element(LISTE *first)


{

Vous aimerez peut-être aussi