Chap. 1 Structures séquentielles : listes linéaires

Download Report

Transcript Chap. 1 Structures séquentielles : listes linéaires

Chap. 1 Structures séquentielles :
listes linéaires
1
Exemple (gestion d'un tableau
contenant les références des livres
d'une bibliothèque.)
• Ce tableau est rangé dans l'ordre alphabétique.
• Lorsqu'un nouveau livre est acheté, son insertion dans le tableau
en respectant l'ordre requiert de déplacer toutes les références qui
suivent la position d'insertion, pour dégager de la place.
• Pour éviter ce type de problème, il faudrait que le passage d'une
case d'un tableau à la suivante ne se fasse plus à partir d'un indice
absolu qu'on incrémente, mais en notant localement dans une
case du tableau l'indice de la case suivante.
2
On organise en liste linéaire des donnéess qui
doivent être traitées séquentiellement.
De plus une liste est évolutive, c.à.d. qu'on
veut pouvoir ajouter et supprimer des
donnéess.
3
Les listes : définition
• Une liste linéaire est une structure de donnéess correspondant à
une suite d'éléments.
• Les éléments ne sont pas indexés dans la liste, mais pour chaque
élément (sauf le dernier) on sait où se trouve l’élément suivant.
•
Par conséquent, on ne peut accéder à un élément qu’en passant
par le premier élément de la liste et en parcourant tous les
éléments jusqu’à ce qu’on atteigne l’élément recherché.
4
La représentation des listes par deux
tableaux
• le premier tableau contient les éléments de la liste, dans un ordre
quelconque.
• le second tableau est organisé de façon suivante : si la case d’indice i du
premier tableau contient l’élément dont le suivant se trouve dans la case
d’indice j, alors la case d’indice i de second tableau contient l’entier j.
5
Exemple
a
0
1
3
0
d
1
b
2
5
\0
3
4
2
2
5
\0
3
4
5
6
Insertion
• Insérons la lettre « c » dans le premier tableau
c
0
a
1
d
2
3
5
0
1
b
2
3
4
0
2
\0
5
\0
3
4
5
7
La représentation chaînée.
• On utilise des pointeurs pour chaîner entre eux les éléments successifs, et
la liste est alors déterminée par l'adresse de son premier élément.
• On va définir des enregistrements (structures) dont un des champs est de
type pointeur vers une structure chaînée du même type.
8
Définition d'une structure chaînée.
typedef struct nœud
{
T info;
struct nœud *suiv;
}nœud;
typedef nœud *liste;
La liste vide est représentée par le pointeur
NULL.
9
aaaaa
bb
ddd
mm
Cette représentation n’impose pas une
longueur maximum sur les listes ; elle
permet de traiter facilement la plupart
des opérations sur les listes : le parcours
séquentiel, l'insertion et la suppression
d'un élément à une place quelconque, la
concaténation de deux listes, se font par
une simple manipulation des pointeurs.
10
Variables dynamiques
• C'est une variable dont la place mémoire est allouée en cours d'exécution.
• On ne prend de la place mémoire que lorsqu'on en a besoin.
• Cette place mémoire est allouée explicitement, c.à.d. par une instruction du
langage.
• Désallocation est aussi proposée par l'intermédiaire d'une instruction.
11
Exemples d’utilisation
12
1. Rechercher un élément dans une liste chaînée juste pour savoir
si cet élément est présent ou non
données : T x, liste p
résultat de type logique (entier en C)
Entête en C : int recherche(T x, liste p)
;
13
{variables locales : logique trouve
trouve  faux
TANT QUE ((pNULL) et (trouve=faux)) faire
{
SI (p->info = x) ALORS
trouve  vrai
SINON
p  p->suiv /*l'adresse de la structure
suivante*/
}
retourner trouve
}
Rmq : Il n’est pas souhaitable de remplacer la condition trouve=faux dans la boucle TANT QUE par
p->infox parce que si p pointe sur NULL,
p->info n'existe pas.
14
2. Créer une liste chaînée par ajout successif d'éléments jusqu'à la fin.
données modifiées : liste *pp
Entête en C : void créer(liste *pp);
15
{
variables locales : liste cour, temp
*pp  NULL
saisir(x)
SI (x  fin)
ALORS
/* fin est une constante de type T */
{
reserver(*pp) /* crée un poste et met l'adresse dans *pp */
*pp->info  x
*pp->suiv  NULL
/* obligé de mettre NULL à chaque fin (même
temporaire) */
cour  *pp /* cour wagon courant (dernier) auquel on rajoute
qqch, cour reçoit *pp, qui est l'adresse du premier élément*/
saisir(x)
TANT QUE (x  fin) FAIRE
{
reserver(temp) /*crée la place mémoire d'un wagon */
temp->info  x
temp->suiv  NULL
1)
cour->suiv  temp
/* création du lien */
2)
cour  temp /* cour doit pointer toujours sur
la dernière structure, on garde ainsi l'adresse
*pp du début de liste */
saisir(x)
}
}
16
}
Autre méthode
.
.
.
TANT QUE (x  fin) FAIRE
{
reserver (cour->suiv)
cour  cour->suiv
cour->info  x
cour->suiv  NULL
saisir(x)
}
.
.
.
/* idem à reserver(temp);
cour->suiv  temp; */
17
2. Supprimer un élément de la liste.
- On suppose que la liste existe
données : T x
données modifiées : liste *pp
résultat de type logique
/* x=info à supprimer, *pp est une données modifiée, car on peut supprimer le
premier de la liste, ok indique si la suppression a eu lieu */
Entête en C : int suppression(T x, liste *pp);
18
{variables locales :
liste prec, cour ; logique ok
ok  faux
SI (*pp->info = x) ALORS
{
prec  *pp
*pp  *pp->suiv
ok  vrai
liberer(prec)
/*suppression du premier
élément */
}
19
SINON
{
prec  *pp
cour  *pp->suiv
TANT QUE ((ok = faux) et (cour  NULL)) FAIRE
{
SI (cour->info = x) ALORS
/*cour pointe sur celui à
supprimer, prec pointe sur le précédent */
{
ok  vrai
prec->suiv  cour->suiv /* création du lien entre
celui qui précède un élément à supprimer et celui qui le suit */
liberer(cour)
}
SINON
{
prec  cour
/* mémorisation de la place
d'élément précédent l'élément à tester */
cour  cour->suiv
}
}
}
}
retourner ok
}
20
3. Insertion d’un élément dans une liste chaînée triée
données T x
données modifiées liste *pp
Entête en C : void insert (T x, liste *pp);
21
{variables locales : ptr_liste cour, prec ; logique trouve
SI (*pp = NULL) ALORS
{
reserver(*pp)
*pp->info  x
*pp->suiv  NULL
}
SINON
SI (x < *pp->info) ALORS
{
reserver(cour)
cour->info  x
cour->suiv  *pp
*pp  cour
}
22
SINON
{
trouve  faux
prec  *pp
cour  *pp->suiv
TANT QUE ((cour  NULL) et (trouve = faux))
FAIRE
{
SI (x < cour->info) ALORS
{
trouve  vrai
reserver(prec->suiv)
prec  prec->suiv
prec->info  x
prec->suiv  cour
}
SINON
{
prec  cour
cour  cour->suiv
}
}
23
SI (cour = NULL) ALORS
{
reserver (prec->suiv)
cour  prec->suiv
cour->info  x
cour->suiv  NULL
}
}
}
24
4. Suppression d’un élément d’une liste chaînée triée
données : T x
données modifiées : liste *pp
résultat de type logique
Entête en C : int suppr (T x, liste *pp);
25
{variables locales :
ptr_liste cour, prec ; logique trouve
trouve  faux
SI (*pp  NULL) ALORS
SI (x = *pp->info) ALORS
{
cour  *pp
*pp  *pp->suiv
liberer (cour)
trouve  vrai
}
26
SINON
{
prec  *pp
cour  *pp->suiv
TANT QUE ((trouve = faux) et (cour  NULL))
FAIRE
{
SI (x = cour->info) ALORS
{
prec->suiv  cour->suiv
liberer (cour)
trouve  vrai
}
SINON
SI (x > cour->info) ALORS
{
prec  cour
cour  cour->suiv
}
SINON
cour  NULL
}
}
retourner trouve
}
27
Il existe d'ailleurs de nombreuses variantes de la représentation des listes à l'aide de pointeurs
a) Il est parfois commode de définir une liste non pas comme un pointeur sur une
cellule, mais par un bloc de cellules du même type que les autres cellules de la liste. Ce
bloc ne contient que l'adresse du premier élément de la liste. L'utilisation d'un tel bloc
permet d'éviter un traitement spécial pour l'insertion et la suppression en début de liste.
b) On peut aussi créer des listes circulaires : on remplace, dans la dernière place de la
liste, le pointeur à NULL par un pointeur vers la tête de la liste ; de plus si l'on choisit la
dernière place comme point d'entrée dans la liste, on retrouve la tête de liste en
parcourant un seul lien.
c) Dans la représentation classique, le parcours des listes est orienté dans un seul sens
: du premier élément vers le dernier élément. Cependant de nombreuses applications
nécessitent de parcourir les listes à la fois vers l'avant et vers l'arrière, et dans ce cas on
peut faciliter le traitement en rajoutant des "pointeurs arrière", ce qui augmente
évidemment la place mémoire utilisée. On obtient alors une liste doublement chaîné :
chaque place comporte un pointeur vers la place suivante et un pointeur vers la place
précédente.
28
Exemple d'utilisation d'une liste
chaînée bidirectionnelle
typedef struct doub
{
T info ;
struct doub *suiv, *prec ;
} doub ;
typedef doub *liste_doub ;
29
Afficher le contenu d'une liste à l'envers.
données : liste_doub p
Entête en C : void afficher(liste_doub p);
{
SI p  NULL ALORS
{
TANT QUE (p->suiv  NULL)
p  p->suiv
TANT QUE (p  NULL) FAIRE
{
afficher (p->info)
p  p->prec
}
}
}
FAIRE
30