Prof. G. Fatima, EST Salé STRUCTURE DE DONNEES … F. Guerouate Université Mohammed V-Agdal École...
-
Upload
ambre-belin -
Category
Documents
-
view
159 -
download
5
Transcript of Prof. G. Fatima, EST Salé STRUCTURE DE DONNEES … F. Guerouate Université Mohammed V-Agdal École...
Prof. G. Fatima, EST Salé
STRUCTURE DE DONNEES …
F. Guerouate
Université Mohammed V-AgdalUniversité Mohammed V-AgdalÉcole Supérieure de École Supérieure de
Technologie SaléTechnologie Salé
Université Mohammed V-AgdalUniversité Mohammed V-AgdalÉcole Supérieure de École Supérieure de
Technologie SaléTechnologie Salé
Prof. G. Fatima, EST Salé2
Chapitre-1Chapitre-1Listes chaînéesListes chaînées
Prof. G. Fatima, EST Salé3
Définition …
Introduction Les listes chaînées sont des structures dont le nombre d’éléments de
même type peut varier au cours de l’exécution du programme.
Les éléments consécutifs seront tout simplement chaînés entre eux. Il existe plusieurs types de listes chaînées dépendant de la manière dont on se déplace dans la liste, les listes chaînées simples, les listes doublement chaînées, les listes circulaires.
Prof. G. Fatima, EST Salé4
Convention graphique
Valeur de l’élément. Place de l ‘élément suivant dans la liste, place est de type pointeur.
valeurvaleur place place
Dans une liste chaînée, un élément est la donnée d’un couple (P, V) , place valeur. Sur une liste chaînée on peut définir trois fonctions :
* Une fonction qui permet de retourner le 1er élément de la liste
* Une fonction qui permet de retourner le successeur d’un élément dans la liste
* Une fonction qui permet de retourner la valeur de l’élément si bien sur la place de ce dernier est connue
Les listes chaînées simples …
Prof. G. Fatima, EST Salé5
Représentation graphique d’une liste chaînée
Déclaration du type pointeur
struct elem {
int valeur ;
struct elem * suivant ; } ;
typedef struct elem liste;
dernierdernier
valeurvaleur ~=~= NILNILpremierpremier
element element
Pointeur sur l’élément suivantPointeur sur l’élément suivant
Prof. G. Fatima, EST Salé6
struct elem *suivant ; déclare un pointeur sur la données suivante. « suivant » est le nom du pointeur (vous pouvez donc mettre n'importe quel nom) et c'est là qu'on découvre à quoi sert l'étiquette. En effet à ce stade le nom de la structure n'est pas encore donné. Toutefois le pointeur va pointer sur un nouvel élément, donc sur une instance de la structure que l'on est justement en train de définir. On utilise donc l'étiquette précédée du mot clef « struct ».
element : il s'agit tout simplement du nom final donnée à la structure.Sur la ligne suivant : on déclare un nouveau type qui est un pointeur vers un element et que l'on appelle « liste ». Cette opération est facultative mais elle accrôit sensiblement la lisibilité du programme.
Prof. G. Fatima, EST Salé7
L'accès à la valeur d'une variable d'un élément donné se fait en reprenant les notations de structure composée.
Accès aux valeurs de la liste …
Exemple liste *l ;
int a ;
a=(*l).valeur ;
liste *l ;
int a ;
a=l->valeur ; OuOu
L'accès au(x) pointeur(s) se fera de la même façon : l->suivant.
Prof. G. Fatima, EST Salé8
Pour cela on utilise l'instruction « malloc » associé à « sizeof » :
liste *l ;
l=(liste*)malloc(sizeof(liste));
Initialiser la liste chaînée …
Notre liste est maintenant initialisée. « l » sera un pointeur sur le premier élément. On peut désormais affecter une valeur à cet élément. Il est important de se souvenir qu'il faudra réserver de la mémoire, donc faire un malloc, à chaque fois que l'on voudra insérer un nouvel élément, à l'emplacement de l'élément.
Prof. G. Fatima, EST Salé9
Admettons que l'utilisateur est entrée au clavier la valeur d'une variable entière «n» indiquant le nombre d'élément à créer. Le programme de saisie pourrait être celui-ci :
Saisie de plusieurs éléments …
Liste *l,*laux ; //on déclare également une variable auxiliaire qui va nous servir pour parcourir la liste
l=(liste*)malloc(sizeof(element)) ;//on initialise c'est à dire on fait pointer l sur l'espace mémoire réservé pour le premier element
laux=l ; //on fait pointer laux au même endroit que l
Prof. G. Fatima, EST Salé10
for(i=1 ;i<n+1 ;i++)
{
//on doit saisir n valeurs
scanf("%d",laux->valeur) ; //saisie de la valeur
//on fait pointer le pointeur suivant sur un nouvel espace réservé
laux->suivant=(liste*)malloc(sizeof(element)) ;
if(i==n)
laux->suivant=NULL ; //on fait pointer le dernier élément sur un pointeur NULL afin d'avoir
un programme plus « propre »
laux=laux->suivant ; //on fait pointer laux sur le nouvel espace créé afin de saisir la
valeur du prochain élément au prochain tour de boucle ;
}
Prof. G. Fatima, EST Salé11
AttentionIl ne faut surtout pas utiliser le pointeur principal « l » pour parcourir les
éléments de la liste. Sinon on perdrai l'adresse du premier élément et donc de toute la liste chaînée.on utilise toujours un pointeur de parcours (liste auxiliaire)
Il est important de savoir qu'il faut toujours faire l'allocation de mémoire AVANT d'ordonner au pointeur de pointer sur un élément.
Prof. G. Fatima, EST Salé12
Si on avait écrit :
laux=laux->suivant ;
laux=(liste*)malloc(sizeof(element)) ;
de même que :
laux=l ;
l=(liste*)malloc(sizeof(element)) ;
la compilation aurait fonctionné mais l'exécution non. En effet
l'adressage du pointeur change au moment de l'allocation de la mémoire. Ceci représente une erreur type. Il faut y faire attention !
N'oubliez pas non plus que toute saisie d'un nouvel élément doit être précédée d'une allocation de mémoire.
Prof. G. Fatima, EST Salé13
En pratique, la saisie se fait rarement par une boucle mais par un appel successif d'une fonction définie par l'utilisateur qui saisie un élément en début ou en fin de liste suivant les cas.
Il n'est pas obligatoire mais il est fortement recommandé de terminer la liste par le pointeur NULL (sauf en cas de liste cyclique, où le dernier pointeur pointe sur le premier élément).
A noter l'existence de la fonction free() ; qui libère la place assignée par un malloc.Ex : free(l) ;
Prof. G. Fatima, EST Salé14
Les opérations sur les listes sont très nombreuses, parmi elles :
- Créer une liste vide
- Tester si une liste est vide
- Ajouter un élément à la liste
· ajouter en début de liste (tête de liste)
· ajouter à la fin de la liste (queue)
· ajouter un élément à une position donnée
· ajouter un élément après une position donnée
. ajouter un élément avant une position donnée
Les opérations sur les listes …
Prof. G. Fatima, EST Salé15
- Afficher ou imprimer les éléments d'une liste
- Ajouter un élément dans une liste triée (par ordre ascendant ou descendant)
- Supprimer un élément d’une liste
· supprimer en début de liste
· supprimer en fin de liste
· supprimer un élément à une position donnée
. supprimer un élément avant ou après une position donnée.
Prof. G. Fatima, EST Salé16
Parcours d’une liste chaînée …
Il existe une liste chaînée d’entiers, préalablement déclarée
But : Afficher dans l’ordre de la liste tous les éléments de la liste : 5-10-15-20-25
5 10 15 20 25
lllauxlaux
l.valeur l.valeur
l.suivant l.suivant
Remarque : Penser à dupliquer la valeur du premier pointeur (laux).
Prof. G. Fatima, EST Salé17
Algorithme
Var *l, *laux : liste
Début
laux :=l
TQ ( laux<>NULL ) FAIRE
Sortir (laux->valeur)
laux:= laux->suivant
FTQ
Remarque : Quand on cherche le dernier élément d‘une liste, le test à effectué est (laux->suivant) <>null. Le test laux<>null permet de parcourir indifféremment tous les éléments de la liste.
Prof. G. Fatima, EST Salé18
Ajout en tête de liste quand la liste n’est vide …
Exemple : On suppose que l’on veut rajouter l’élément 6 au début de la liste suivante :
4 3 5 8 2
lla)- on commence par créer le nouveau nœud et lui donner son contenu
6
lauxlaux
Prof. G. Fatima, EST Salé19
b)- le suivant de p est l’ancienne tête de liste
6 4 3 5 8 2
lauxlaux ll
b)- on fait pointer le pointeur l vers la tête de liste
6 4 3 5 8 2
ll
Prof. G. Fatima, EST Salé20
Les opérations a, b et c se traduisent par le pseudo-code suivant : Allouer (laux)
Laux-> valeur := 6
Laux-> suivant := l
l := laux
Remarque : Si la liste est vide, cet algorithme convient aussi sous réserve d’avoir correctement initialiser premier
Prof. G. Fatima, EST Salé21
Ajout en fin de liste (liste non vide) …
a)- on cherche le dernier élément de la liste, pour cela on utilise un pointeur de parcours.
b)- création du nouveau bloc à insérer dans la liste.(nouveau)
c)- lier le nouveau bloc à la liste.
Prof. G. Fatima, EST Salé22
q := l
TQ (q ->Suivant <>nil) FAIRE
q := q -> Suivant
FTQ
Allouer (nouveau)
Entrer (nouveau ->Valeur)
nouveau ->suivant := nil
q ->suivant := nouveau
Prof. G. Fatima, EST Salé23
Dans cette situation, le pointeur nouveau n’est pas indispensable car le pointeur de parcours q pointe sur le dernier bloc.Comment ? .
Allouer (q-> Suivant)
q := q-> Suivant
q-> Suivant := nil
entrer (q -> valeur)
Prof. G. Fatima, EST Salé24
Ajout dans une liste chaînée …
pp
Ajout d’un élément après un nœud pointé par p
La recherche a été effectuée est fournie le pointeur sur le bloc qui précède celui à insérer.
Prof. G. Fatima, EST Salé25
a)- création du nouveau bloc
b)- mise à jour des liens.
Allouer (nouveau)
Entrer (nouveau -> Valeur)
nouveau ->Suivant := laux-> suivant
Laux->suivant := nouveau
Prof. G. Fatima, EST Salé26
Ajout d’un élément avant un nœud pointé par p
pp
17 -6 9 34
Vers le nouvel élémentVers le nouvel élément
Si la valeur du nouvel élément est -5, le successeur du prédécesseur de p (-6) devient le nouvel élément (-5), or nous ne pouvons pas remonter un pointeur à reculons, il faudrait repartir au début de la liste. Une solution serait d’insérer le nœud après p et transférer la valeur de nœud p dans le nouvel élément.
Prof. G. Fatima, EST Salé27
17 -6 -5 9 34
Noter qu’avec cette solution le pointeur p qui pointait vers le nœud contenant 9 avant l’insertion du nouvel élément, pointe vers le nœud contenant –5 après l’insertion du nouvel élément.
Prof. G. Fatima, EST Salé28
Suppression dans une liste chaînée …
On ne peut pas détruire un élément dans une liste vide.
Suppression en tête de liste
-5 15 35 45ll
TempTemp
l := l-> Suivant
Si on se content de cette instruction, le programme a logiquement éliminé
de la liste le premier bloc, mais celui-ci est toujours en mémoire. De ce fait, il occupe inutilement de la place mémoire. Il faut donc libérer cette place mémoire.
Prof. G. Fatima, EST Salé29
La fonction free, en langage C, permet de libérer cette place mémoire. Cette fonction prend un paramètre en l’occurrence, le pointeur de bloc qui doit être supprimé.
Temp := l
l := l-> Suivant
Libérer (Temp) (Temp ne pointe sur rien du tout)
Prof. G. Fatima, EST Salé30
Suppression en fin de liste
a)- Parcours de la liste à le recherche du dernier élément (sous réserve d’avoir en mémoire l’adresse du dernier élément).
b)- L’avant dernier bloc de la liste devient le dernier.
c)- On libère le bloc à supprimer.
dernier := l
TQ (dernier-> Suivant) <> nil FAIRE
l := dernier
dernier := dernier-> Suivant
FTQ
l-> Suivant := nil
Libérer (dernier)
dernier := l
Prof. G. Fatima, EST Salé31
Cet algorithme ne fonctionne pas si la liste est vide.
Prof. G. Fatima, EST Salé32
Suppression à un endroit quelconque de la liste
a)- Recherche du bloc qui précède le bloc à supprimer.
b)- Construire le lien qui unis le bloc qui précède le bloc à détruire et le bloc qui le succède.
c)- Détruire.
parcours := l
TQ (parcours->suivant <> R) FAIRE
parcours := parcours->Suivant
FTQ
parcours-> Suivant := R -> Suivant
Libérer ( R )
Prof. G. Fatima, EST Salé33
LISTES PARTICULIERES …
Prof. G. Fatima, EST Salé34
LISTES BILATERES …
Définition
Dans les listes simplement chaînées, à partir d'un nœud donné, on ne peut accéder qu'au successeur de ce nœud. Dans une liste doublement chaînée (ou bilaterè), à partir d'un nœud donné, on peut accéder au nœud successeur et au nœud prédécesseur.
Les éléments d’une liste bilatère contiennent 2 pointeurs :
*un pointeur sur le bloc suivant
*un pointeur sur le bloc précédent
30 35 34
Tête Tête Courant Courant Queue Queue
Prof. G. Fatima, EST Salé35
Déclaration des types de données nécessaires
struct elem {
int valeur ;
struct elem * suivant;
struct elem *precedent; } ;
typedef struct elem * liste;
Prof. G. Fatima, EST Salé36
Construction De La Liste
A)- liste vide
l : =NIL
B)- Ajout du premier élément
allouer(l)l->suivant:=NILL->prec=NULL
entrer (l->valeur)
C)- Ajout entête de liste si la liste n’est pas vide.
ALLOUER (laux)
laux->suivant : =l
l->précédent : = laux
laux->précédent : = NIL
laux->valeur : = 1
l : =laux
Prof. G. Fatima, EST Salé37
D)- Ajout en fin de liste dans les listes bilatères.
But : insérer une valeur dans une liste triée en ordre croissant
a)- Phase de parcours (à la recherche du dernier bloc).
b)- Création du nouveau bloc (à insérer).
d)- Mise à jour des liens.
Prof. G. Fatima, EST Salé38
laux : =l ;
TQ laux->suivant <> NIL faire
laux: =laux->suivant
FTQ
Allouer (nouveau)
Entrer (nouveau-> Valeur)
nouveau->suivant : = NIL
nouveau->précédent : = laux
Laux->suivant : = nouveau
Prof. G. Fatima, EST Salé39
E)- Ajout à un endroit qcq de la liste.
20
10
23 12
CourantCourant
Prof. G. Fatima, EST Salé40
laux : = l
TQ laux-> Valeur <>20 FAIRE
laux : = laux->suivant
FTQ
Allouer (nouveau)
Entrer (nouveau->valeur)
nouveau->précédent : = laux
nouveau ->suivant : = laux->suivant
Laux->suivant ->précédent : = nouveau
Laux->suivant : = nouveau
Prof. G. Fatima, EST Salé41
Destruction dans une liste bilatère …
Suppression en tête de liste
(*) toujours s’assurer qu’il existe 1 elt dans la liste.
laux :=l
l:= l->suivant
//l->suivant->précédent : =NIL
Dispose (laux)
//Libérer (l-> Précédent)
l->Précédent : = NIL
Prof. G. Fatima, EST Salé42
Suppression en fin de liste
laux :=l
TQ laux->suivant <>NIL faire
laux :=laux->suivant
FTQ
Laux->précédent->suivant : =NIL
Libérer(laux)
Attention cet algorithme n’est valable que si la liste contient plus d’un élément.
Prof. G. Fatima, EST Salé43
Suppression à un endroit quelconque de la liste
s->precedent>suivant=s-> suivant
s ->suivant ->precedent: =s->precedent
libérer (s).
Prof. G. Fatima, EST Salé44
LISTES CIRCULAIRES …
Prof. G. Fatima, EST Salé45
Définition
Une liste où le pointeur NUL du dernier élément est remplacé par l’adresse du premier élément est appelée liste circulaire.
Dans une liste circulaire tous les nœuds sont accessibles à partir de n’importe quel autre nœud. Une liste circulaire n’a pas de premier et de dernier nœud.
Une liste circulaire peut être simplement chaînée ou doublement chaînée.
Noter que la concaténation de deux listes circulaires peut se faire sans avoir à parcourir les deux listes.
Prof. G. Fatima, EST Salé46
Déclaration des types de données nécessaires
typedef struct elem {
int valeur ;
struct elem * suivant;} element ;
typedef element * anneau;