prof. g. fatima, est salé structure de donnees … f. guerouate université mohammed v-agdal École...

Post on 04-Apr-2015

158 Views

Category:

Documents

5 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

qq

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;

top related