CH 2 Suite 1 - Les Listes Doublement Chainées

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 17

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017

Universit Stif 1
Facult des Sciences
Dpartement dinformatique
Filire : Licence Acadmique
Module : Algorithmique et structure de donnes
Anne universitaire : 2016-2017
CH2 suite 1 : Les listes doublement chaines
Lorsque chaque lment d'une liste chaine pointe vers l'lment suivant, nous parlons de liste simplement
chaine. Lorsque chaque lment d'une liste pointe la fois vers l'lment suivant et prcdent, nous parlons
alors de liste doublement chaine ou liste symtrique. Retenez donc qu'une liste chaine nous permet de
stocker un nombre inconnu d'lments.
Voici une reprsentation schmatique des listes doublement chaines:

Vous pouvez donc voir sur ce schma que chaque lment d'une liste doublement chaine contient :
Une donne (ici un simple entier)
Un pointeur vers l'lment suivant (NULL si l'lment suivant n'existe pas)
Un pointeur vers l'lment prcdent (NULL si l'lment prcdent n'existe pas)
Passons maintenant la reprsentation de ces listes en langage C.

Reprsentation d'une liste en langage C


Quallons-nous utiliser pour nous reprsenter ces listes en langage C. La rponse est toute simple. Nous
allons tout simplement utiliser une structure. En effet, en langage C, les structures sont trs pratiques pour
crer de nouveaux types de donnes. Pour tre plus prcis, nous allons utiliser exactement deux structures.
Voici la premire :
Code : C
1 struct node
2{
3
int data;
4
struct node *p_next;
5
struct node *p_prev;
6 };

Cette premire structure va nous permettre de reprsenter un 'node' (lment) de notre liste chane. Nous
pouvons alors voir que chaque lment de notre liste contiendra un lment de type int. D'autre part :
p_next pointera vers l'lment suivant (ou NULL s'il s'agit du dernier lment de la liste)
p_prev pointera vers l'lment prcdent (ou NULL s'il s'agit du premier lment)
Les liens entre les diffrents lments de notre liste chane sont donc assurs par nos deux pointeurs p_next
et p_prev.
1

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


Afin de bien visualiser nos pointeurs, nous utiliserons le prfixe p_ pour toutes nos variables pointeurs.
Pour reprsenter notre liste chane proprement parler, nous utiliserons une deuxime liste que voici :
Code : C
1 typedef struct dlist
2{
3
size_t length;
4
struct node *p_tail;
5
struct node *p_head;
6 } Dlist;

Attends attends, c'est quoi a size_t ? Et a veut dire quoi p_tail et p_head ?
Tout d'abord, nous utilisons une variable nomme length contenant la taille de notre liste chane (length =
taille en anglais). Grce cette variable, nous aurons accs au nombre d'lments de notre liste chane.
Cependant, cette variable peut paratre un peu particulire puisqu'elle est de type size_t. Ce fameux size_t
correspond un entier non sign c'est dire un entier positif (a tombe bien car notre liste chane ne pourra
pas contenir -1 lment). Ce type est de ce fait communment utilis pour tout ce qui concerne les tailles
(taille d'un tableau, ...).
Enfin, claircissons ce fameux p_tail et p_head, quoi vont-ils bien nous servir ? Ceci est tout simple.
p_head va pointer vers le premier lment de notre liste alors que p_tail va pointer vers le dernier lment.
Ainsi, nous garderons de manire permanente un pointeur vers le dbut et la fin de la liste.
Ok, mais quoi a va nous servir ?
Et bien cela va tout simplement servir faciliter les diffrentes oprations que nous effectuerons sur nos
listes. En effet, pour exemple, lorsque nous souhaiterons ajouter un lment en fin de liste, nous n'aurons pas
besoin de parcourir la liste dans sa totalit pour ajouter l'lment en fin car nous disposerons directement
d'une rfrence vers la fin de liste.
Enfin, afin de faciliter l'criture, nous crons un alias de notre structure grce l'oprateur typedef. Nous
appelons cet alias Dlist pour Double List. Pour utiliser une liste dans nos programmes nous utiliserons alors :
Code : C
1 Dlist *list = NULL; /* Dclaration d'une liste vide */

Manipulation d'une liste doublement chaine (1/2)


Nous savons dsormais comment dclarer une liste doublement chane en langage C. Nous allons
maintenant crer des fonctions nous permettant de raliser plusieurs oprations sur ces fameuses listes.
Avant de regarder le code, je vous conseille de raliser ces fonctions par vous-mme, vous progresserez plus
vite.
Commenons alors par notre premire fonction.

Allouer une nouvelle liste


Avant de pouvoir commencer utiliser notre liste chane, nous allons crer une fonction nous permettant
d'allouer de l'espace mmoire pour notre liste chane. La fonction retournera la liste chane nouvellement
cre. Voici cette fameuse fonction
Code : C
1 Dlist *dlist_new(void)
2{
3
Dlist *p_new = malloc(sizeof *p_new);
4
if (p_new != NULL)

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


5
6
7
8
9
10
11 }

{
p_new->length = 0;
p_new->p_head = NULL;
p_new->p_tail = NULL;
}
return p_new;

Pour respecter une certaine convention, toutes nos fonctions seront de la forme dlist_.
Comment fonctionne cette fonction ? Je pense que vous l'aurez devin sans trop de problmes. Tout d'abord,
nous crons une variable p_new qui sera notre nouvelle liste. Nous utilisons alors malloc pour rserver de
l'espace mmoire pour cette liste.
La syntaxe suivante:
Code : C
1 int *p_data = malloc(sizeof *p_data);

Est identique :
Code : C
1 int *p_data = malloc(sizeof(int));

De cette manire, si l'on modifie notre type, on n'aura pas besoin de le modifier dans notre malloc.
Ensuite et de manire gnrale, il est ncessaire de vrifier si notre malloc n'a pas chou. En effet, si celuici renvoie NULL, et que nous essayons d'accder aux lments de notre structure Dlist, c'est le drame.
Enfin, nous mettons nos pointeurs p_head ainsi que p_tail NULL (vu que notre liste est vide), puis nous
initialisons la taille de notre liste 0 et nous retournons notre nouvelle liste.

Ajouter un lment
Aprs avoir allou une nouvelle liste chane, voyons maintenant comment ajouter un lment dans celle-ci.
Ajout en fin de liste
Grce la forme de notre structure, l'ajout en fin de liste va tre simplifi. En effet, rappelez-vous, nous
gardons toujours un pointeur vers la fin de notre liste, nous n'avons donc nul besoin de parcourir la liste en
entier afin d'arriver au dernier lment, nous l'avons dj. Voici comment va se passer l'ajout en fin de liste:

A partir de ce schma, essayons d'en dduire un algorithme. Tout d'abord, nous devons vrifier si notre liste
n'est pas NULL. Si elle ne l'est pas, nous allons crer un nouvel lment (nouveau node). Une fois celui-ci
cr, nous devons stoquer notre donne dans le champ donne (data) de notre structure puis faire pointer
p_next vers NULL car ce sera le dernier lment de notre liste. A partir de l, deux possibilits s'offrent
nous :
3

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017

S'il n'existe pas de dernier lment (donc la liste est vide) Alors
o Nous faisons pointer p_prev vers NULL
o Nous faisons pointer la tte et la fin de liste vers notre nouvel lment
Sinon
o Nous rattachons le dernier lment de notre liste notre nouvel lment (dbut du chanage)
o Nous faisons pointer p_prev vers le dernier lment de notre liste
o Nous faisons pointer notre fin de liste vers notre nouvel lment (fin du chanage)
Enfin, nous incrmentons notre champ length de notre liste puis nous retournons la liste. Tout ceci constitue
alors l'algorithme d'ajout en fin de liste. Essayez tout d'abord de le coder par vous mme, cela sera bnfique
pour vous et vous aidera comprendre le concept. Si vous bloquez, munissez-vous dune feuille et dun
crayon et essayez de reprsenter toutes les tapes sur votre feuille. Ensuite, ressayez de coder l'algorithme.
Voici l'implmentation :
Code : C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Dlist *dlist_append(Dlist *p_list, int data)


{
if (p_list != NULL) /* On vrifie si notre liste a t alloue */
{
struct node *p_new = malloc(sizeof *p_new); /* Cration d'un nouveau node */
if (p_new != NULL) /* On vrifie si le malloc n'a pas chou */
{
p_new->data = data; /* On 'enregistre' notre donne */
p_new->p_next = NULL; /* On fait pointer p_next vers NULL */
if (p_list->p_tail == NULL) /* Cas o notre liste est vide (pointeur
vers fin de liste NULL) */
{
p_new->p_prev = NULL; /* On fait pointer p_prev vers NULL */
p_list->p_head = p_new; /* On fait pointer la tte de liste vers le
nouvel lment */
p_list->p_tail = p_new; /* On fait pointer la fin de liste vers le
nouvel lment */
}
else /* Cas o des lments sont dj prsents dans notre liste */
{
p_list->p_tail->p_next = p_new; /* On relie le dernier lment de la
liste vers notre nouvel lment (dbut du chanage) */
p_new->p_prev = p_list->p_tail; /* On fait pointer p_prev vers le
dernier lment de la liste */
p_list->p_tail = p_new; /* On fait pointer la fin de liste vers
notre nouvel lment (fin du chanage: 3 tapes) */
}
p_list->length++; /* Incrmentation de la taille de la liste */
}
}
return p_list; /* on retourne notre nouvelle liste */
}

Le code ci-dessus est entirement comment, il ne sera donc pas ncessaire d'ajouter de commentaires.
N'hsitez pas relire ce morceau de code. Si vous avez des difficults le comprendre, jouez le rle du
compilateur et imaginez vous le droulement de chaque instruction.
Ajout en dbut de liste
Pour ajouter un lment en dbut de liste, nous allons utiliser exactement le mme procd que pour l'ajout
en fin de liste. Et oui, grce nos pointeurs en dbut et en fin de liste, nous pouvons nous permettre de
reprendre nos implmentations. Si vous avez bien compris comment se passait l'ajout en fin de liste, vous
n'aurez aucun de mal raliser l'ajout en dbut de liste. L aussi, essayez d'abord par vous mme de
programmer cet algorithme.
4

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


Voici la fonction finale :
Code : C
1 Dlist *dlist_prepend(Dlist *p_list, int data)
2{
3
if (p_list != NULL)
4
{
5
struct node *p_new = malloc(sizeof *p_new);
6
if (p_new != NULL)
7
{
8
p_new->data = data;
9
p_new->p_prev = NULL;
10
if (p_list->p_tail == NULL)
11
{
12
p_new->p_next = NULL;
13
p_list->p_head = p_new;
14
p_list->p_tail = p_new;
15
}
16
else
17
{
18
p_list->p_head->p_prev = p_new;
19
p_new->p_next = p_list->p_head;
20
p_list->p_head = p_new;
21
}
22
p_list->length++;
23
}
24
}
25
return p_list;
26 }

Il y a comme des ressemblances entre les deux fonctions vous ne trouvez pas.

Insrer un lment
Nous disposons dsormais de fonctions permettant d'ajouter un lment en dbut ainsi qu'en fin de liste.
Mais si l'on dsire ajouter un lment nimporte o dans notre liste ? Et bien nous allons justement crer une
fonction pour ceci. Comment allons-nous procder ? Posons-nous et rflchissons cinq minutes. Tout
d'abord, nous aurons besoin de parcourir notre liste. Nous aurons aussi besoin d'un compteur (que l'on
nommera) i afin de nous arrter la position o nous souhaitons insrer notre nouvel lment. Jusqu'ici, rien
de bien sorcier. Il nous faut alors rflchir des diffrents cas de figure qui peuvent intervenir lorsque nous
aurons trouv notre position:
Soit nous sommes en fin de liste
Soit nous sommes en dbut de liste
Soit nous sommes en milieu de liste
Cependant, les deux premiers cas sont trs faciles trater. Enfin, nous disposons de fonctions permettant
d'ajouter un lment en dbut et en fin de liste, il nous suffit donc de les raliser. Le plus gros de notre travail
sera alors de grer le cas o nous nous trouvons en milieu de liste. Voici un petit schma permettant de
mieux cerner la situation:

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017

Le chanage va tre lgrement plus compliqu. En effet, nous devrons tout d'abord relier nos lments
suivant et prcdent notre nouvel lment puis, inversement, nous devrons relier notre nouvel lment aux
lments suivant et prcdent. Le chanage va alors se drouler en 4 tapes. A noter qu'il sera ncessaire
d'avoir pralablement cr un nouvel lment sans quoi le chanage ne pourra pas avoir lieu.
Pour parcourir notre liste, nous rcuprerons le pointeur vers notre dbut de liste dans un pointeur
temporaire. C'est ce pointeur temporaire qui nous servira parcourir notre liste. Schmatiquement, notre liste
sera parcourue de gauche droite. Notre compteur sera bien videmment incrment lors du parcours de
chaque maillon de la liste.
Voici ce que cela donne:
Code : C
1 Dlist *dlist_insert(Dlist *p_list, int data, int position)
2{
3
if (p_list != NULL)
4
{
5
struct node *p_temp = p_list->p_head;
6
int i = 1;
7
while (p_temp != NULL && i <= position)
8
{
9
if (position == i)
10
{
11
if (p_temp->p_next == NULL)
12
{
13
p_list = dlist_append(p_list, data);
14
}
15
else if (p_temp->p_prev == NULL)
16
{
17
p_list = dlist_prepend(p_list, data);
18
}
19
else
20
{
21
struct node *p_new = malloc(sizeof *p_new);;
22
if (p_new != NULL)
23
{
24
p_new->data = data;
25
p_temp->p_next->p_prev = p_new;
26
p_temp->p_prev->p_next = p_new;
27
p_new->p_prev = p_temp->p_prev;
28
p_new->p_next = p_temp;
29
p_list->length++;
30
}
31
}
32
}
33
else
34
{
35
p_temp = p_temp->p_next;
36
}

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


37
38
39
40
41 }

i++;
}
}
return p_list;

Si vous avez compris les codes prcdemment tablis, je ne pense pas que vous aurez des difficults
comprendre celui-ci. Cependant, voici quelques explications supplmentaires: pour notre parcours de liste,
nous utilisons un pointeur nomm p_temp. Au tout dbut, celui-ci pointe vers le premier lment de notre
liste (p_list->p_head). Pour parcourir notre liste, nous utilisons une structure de type while. Tant que nous
n'avons pas atteint la fin de liste (p_temp != NULL) et tant que nous ne sommes pas la position o nous
voulons insrer notre lment (position <= i), nous bouclons. Ds lors que nous avons atteint notre position
(position == i), nous devons alors effectuer nos trois tests :
Si nous sommes en fin de liste (p_temp->p_next == NULL), nous utilisons notre fonction
dlist_append
Sinon, si nous sommes en dbut de liste (p_temp->p_prev == NULL), nous utilisons notre fonction
dlist_prepend
Sinon, nous devons crer un nouvel lment et raliser notre chanage sans oublier de stoquer la
donne dans notre champ data
Logiquement, grce au schma prcdent et aux explications fournies, vous ne devriez pas avoir de mal
comprendre le chanage.
Enfin, si nous n'avons pas encore atteint notre position, nous passons l'lment suivant (p_temp = p_temp>p_next).

Librer une liste


Aprs avoir utilis notre liste, nous nous devons de librer tous nos lments allous par nos fonctions sous
peine d'obtenir ce que l'on nomme des fuites de mmoire (leak memory). Pour accomplir ceci, rien de plus
facile. Regardons le code:
Code : C
1 void dlist_delete(Dlist **p_list)
2{
3
if (*p_list != NULL)
4
{
5
struct node *p_temp = (*p_list)->p_head;
6
while (p_temp != NULL)
7
{
8
struct node *p_del = p_temp;
9
p_temp = p_temp->p_next;
10
free(p_del);
11
}
12
free(*p_list), *p_list = NULL;
13
}
14 }

Dans ce code, nous pouvons remarquer qu'une petite chose change par rapport nos codes prcdents. Dans
cette fonction, nous utilisons un double pointeur. En effet, notre fonction delete doit directement effectuer
les modifications sur notre liste. Autrement dit, celle-ci doit faire des modifications sur un objet de type Dlist
*. C'est pour cela que nous ne devons pas disposer d'un pointeur simple, mais bel et bien d'un pointeur
double.
En premier lieu, nous vrifions si la liste que nous avons rcupre n'est pas NULL. Si celle-ci venait tre
NULL et que nous essayions de la manipuler, nous aboutirions un beau plantage. Nous parcourons ensuite
chaque lment de la liste comme dans notre fonction prcdente ( noter la prsence de parenthses afin de
rsoudre la priorit de l'oprateur -> sur l'oprateur *). Seulement, nous prenons garde de sauvegarder
l'lment courant dans un pointeur p_del (qui sera comme vous l'aurez compris l'lment que nous
7

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


supprimerons). En effet, si nous n'utilisons pas de pointeur intermdiaire, lorsque nous supprimerons notre
premier lment, p_temp->p_next n'existera plus puisque notre lment aura t supprim, nous aurons donc
un beau plantage. C'est pour cela que nous sauvegardons d'abord notre lment courant, puis nous passons
l'lment suivant et enfin nous supprimons notre lment courant (p_del). Quand tous les lments sont
supprims, nous terminons par supprimer notre liste puis nous la remettons NULL.

Exercice
Passons maintenant un petit exercice afin de mettre en uvre vos connaissances. Ecrivez une fonction
dlist_display prenant en paramtre une liste et affichant tous les lments de la liste spars par des '>'.
Lorsque la fin de liste sera atteinte, NULL sera affich. Exemple :
Code : Console
4 > 8 > 15 > 16 > 23 > 42 > NULL

Correction:
Code : C
1 void dlist_display(Dlist *p_list)
2{
3
if (p_list != NULL)
4
{
5
struct node *p_temp = p_list->p_head;
6
while (p_temp != NULL)
7
{
8
printf("%d -> ", p_temp->data);
9
fflush(stdout);
10
p_temp = p_temp->p_next;
11
}
12
}
13
printf("NULL\n");
14 }

Comme vous pouvez le voir, ceci n'a rien de sorcier. Il suffit tout simplement de parcourir la liste
entirement puis d'afficher les lments un un. Lorsque la boucle est termine, nous terminons par afficher
"NULL".
Notre premier printf n'tant pas termin par un '\n', nous nous devons de forcer l'affichage des caractres par
l'utilisation de fflush(stdout).

Manipulation d'une liste doublement chaine (2/2)


Dans la partie prcdente, nous avons ralis quelques fonctions basiques de manipulation de listes chaines.
Nous bnficions dsormais de fonctions d'allocation, ajout, insertion ainsi que de libration de liste. Nous
allons alors complter notre petite bibliothque en crant des fonctions supplmentaires. Nous allons tout
d'abord nous occuper de fonctions de suppression.

Supprimer un lment d'une liste


Supprimer un lment selon sa valeur
Nous allons tout d'abord voir la manire de supprimer un lment en fonction de sa valeur. A nouveau, voici
un schma pour vous aider mieux visualiser le procd:

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017

Ici, nous dcidons de supprimer l'lment portant la valeur 15. Comment allons-nous nous y prendre ? Tout
d'abord, comme vous pourrez l'imaginer, il va nous falloir parcourir notre liste la rechercher de notre
lment supprimer. Ds que l'on aura trouv la valeur correspondante, trois possibilits s'offriront nous:
l'lment se trouve en fin de liste
l'lment se trouve en dbut de liste
l'lment se trouve en milieu de liste
Si l'lment se trouve en fin de liste, Alors il faudra faire pointer notre p_tail vers l'avant dernier lment et
faire pointer le pointeur vers l'lment suivant de l'avant dernier lment vers NULL.
Sinon, si l'lment se trouve en fin de liste, Alors il faudra faire pointer notre p_head vers le second lment
et faire pointer le pointeur vers l'lment prcdent du second lment vers NULL.
Sinon, il faudra relier l'lment prcdent l'lment que l'on veut supprimer vers l'lment suivant
l'lment que l'on veut supprimer et il faudra aussi relier l'lment suivant l'lment su l'on veut supprimer
vers l'lment prcdent l'lment que l'on veut supprimer.
Une fois ceci fait, il ne nous restera plus qu' supprimer notre lment trouver et dcrmenter la taille de
notre liste.
Tout ceci constitue notre algorithme. A sa lecture, celui-ci peut sembler rebutant mais lors de la ralisation
en langage C, il vous deviendra beaucoup plus clair.
Avec ces explications, vous devriez tre capable de raliser le code tout seul.
Notre fonction ne supprimera que le premier lment trouv.
Code : C
1 Dlist *dlist_remove(Dlist *p_list, int data)
2{
3
if (p_list != NULL)
4
{
5
struct node *p_temp = p_list->p_head;
6
int found = 0;
7
while (p_temp != NULL && !found)
8
{
9
if (p_temp->data == data)
10
{
11
if (p_temp->p_next == NULL)
12
{
13
p_list->p_tail = p_temp->p_prev;
14
p_list->p_tail->p_next = NULL;
15
}

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 }

else if (p_temp->p_prev == NULL)


{
p_list->p_head = p_temp->p_next;
p_list->p_head->p_prev = NULL;
}
else
{
p_temp->p_next->p_prev = p_temp->p_prev;
p_temp->p_prev->p_next = p_temp->p_next;
}
free(p_temp);
p_list->length--;
found = 1;
}
else
{
p_temp = p_temp->p_next;
}
}
}
return p_list;

Vous voyez donc que traduit en langage C, l'algorithme devient plus intuitif. A noter cependant que nous
utilisons une variable supplmentaire nomme found pour nous arrter au premier lment trouv. Lorsque
l'lment est trouv, cette variable change d'tat et prend la valeur 1, marquant ainsi l'arrt de la boucle de
parcours.
Supprimer un ensemble d'lments suivant une mme valeur
L'algorithme prcdent ne nous permettait de supprimer uniquement le premier lment trouv. Nous allons
maintenant crire un code supprimant toutes les valeurs trouves dans la liste. Et devinez quoi ? Et bien
comme vous pouvez vous en douter, il s'agit exactement du mme code que prcdemment, mis part le fait
qu'ici nous n'utilisons plus de variable found, mais nous parcourons notre liste dans sa totalit.
Code : C
1 Dlist *dlist_remove_all(Dlist *p_list, int data)
2{
3
if (p_list != NULL)
4
{
5
struct node *p_temp = p_list->p_head;
6
while (p_temp != NULL)
7
{
8
if (p_temp->data == data)
9
{
10
struct node *p_del = p_temp;
11
p_temp = p_temp->p_next;
12
if (p_del->p_next == NULL)
13
{
14
p_list->p_tail = p_del->p_prev;
15
p_list->p_tail->p_next = NULL;
16
}
17
else if (p_del->p_prev == NULL)
18
{
19
p_list->p_head = p_del->p_next;
20
p_list->p_head->p_prev = NULL;
21
}
22
else
23
{
24
p_del->p_next->p_prev = p_del->p_prev;
25
p_del->p_prev->p_next = p_del->p_next;

10

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


26
27
28
29
30
31
32
33
34
35
36
37 }

}
free(p_del);
p_list->length--;
}
else
{
p_temp = p_temp->p_next;
}
}
}
return p_list;

Supprimer un lment selon sa position


Passons maintenant la dernire fonction de suppression. Nous allons crer une fonction permettant de
supprimer le n-me lment d'une liste doublement chane. Comment procder ? Et bien il s'agit exactement
du mme procd utilis prcdemment, mis part le fait que nous aurons besoin d'une variable
supplmentaire nous permettant de stoquer la position laquelle nous nous trouvons. Et comme par hasard,
nous allons nommer cette variable i. Au vu de ce que nous avons ralis prcdemment, vous devriez
parvenir crire ce bout de code par vous mme. Essayez et persvrez.
Voici la dite fonction:
Code : C
1 Dlist *dlist_remove_id(Dlist *p_list, int position)
2{
3
if (p_list != NULL)
4
{
5
struct node *p_temp = p_list->p_head;
6
int i = 1;
7
while (p_temp != NULL && i <= position)
8
{
9
if (position == i)
10
{
11
if (p_temp->p_next == NULL)
12
{
13
p_list->p_tail = p_temp->p_prev;
14
p_list->p_tail->p_next = NULL;
15
}
16
else if (p_temp->p_prev == NULL)
17
{
18
p_list->p_head = p_temp->p_next;
19
p_list->p_head->p_prev = NULL;
20
}
21
else
22
{
23
p_temp->p_next->p_prev = p_temp->p_prev;
24
p_temp->p_prev->p_next = p_temp->p_next;
25
}
26
free(p_temp);
27
p_list->length--;
28
}
29
else
30
{
31
p_temp = p_temp->p_next;
32
}
33
i++;
34
}
35
}
36
return p_list;

11

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


37 }

Comme vous le voyez, tant que nous n'avons pas atteint la fin de liste, et tant que nous ne sommes pas la
bonne position, nous bouclons et nous incrmentons notre variable i. Lorsque nous avons trouv la bonne
place (position == i), nous supprimons alors notre lment courant en suivant exactement la mme mthode
que prcdemment.

Avoir la taille d'une liste chane


Voyons maintenant comment obtenir la taille de notre liste chane. Rien de plus facile, allez-vous me dire,
nous disposons dj d'un champ length dans notre structure, nous allons donc l'utiliser. La rponse est
vidente, nanmoins, il faudra au pralable vrifier si notre liste n'est pas nulle. En effet, si tel est le cas et
que nous essayons d'accder au champ length, alors nous aboutirons une belle erreur de Segmentation.
Prenez donc garde vrifier la validit de la liste.
Voici la fonction
Code : C
1 size_t dlist_length(Dlist *p_list)
2{
3
size_t ret = 0;
4
if (p_list != NULL)
5
{
6
ret = p_list->length;
7
}
8
return ret;
9}

Je ne pense pas que ce code ncessite une explication supplmentaire, vous devriez aisment le comprendre
(nous utilisons juste une variable ret de type size_t pour retourner le rsultat. Par dfaut, nous renvoyons 0 si
la liste n'existe pas).
Maintenant que nous bnficions d'une fonction permettant d'effacer un lment selon sa position ainsi que
d'une fonction permettant de retourner la taille de notre liste, nous pouvons aisment construire deux autres
fonctions dont le rle sera de supprimer le premier ainsi que le dernier lment de la liste. Pour cela, j'ai
choisi d'employer les macros:
Code : C
1 #define dlist_remove_first(list) dlist_remove_id(list, 1)
2 #define dlist_remove_last(list) dlist_remove_id(list, dlist_length(list))

Ainsi, l'appel dlist_remove_first et dlist_remove_last sera en fait remplac directement dans le code par un
appel dlist_remove_id. Pratique, n'est-ce pas?

Rechercher un lment
Recherche un lment selon sa valeur
Pour complter notre petite bibliothque, il se peut que nous ayons besoin d'une fonction de recherche. Cette
fonction de recherche sera un tout petit peu particulire. En effet, celle-ci ne renverra pas l'lment qu'elle
aura trouv mais une liste contenant l'lment qu'elle aura trouv. Ceci facilitera de ce fait notre gestion.
Comment faire ? Vous vous souvenez de la fonction de suppression ? Et bien nous allons utiliser le mme
style. Nous allons parcourir notre liste tant que nous n'aurons pas trouv notre lment (variable found). Si
nous trouvons notre lment, nous utilisons alors les fonctions dj notre disposition (dlist_new et
dlist_append) pour crer notre liste qui sera retourn.
Code : C
1 Dlist *dlist_find(Dlist *p_list, int data)

12

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


2{
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 }

Dlist *ret = NULL;


if (p_list != NULL)
{
struct node *p_temp = p_list->p_head;
int found = 0;
while (p_temp != NULL && !found)
{
if (p_temp->data == data)
{
ret = dlist_new();
ret = dlist_append(ret, data);
found = 1;
}
else
{
p_temp = p_temp->p_next;
}
}
}
return ret;

Dsormais, cette fonction doit vous paratre anodine. Si jamais nous ne trouvons aucun lment
correspondant une valeur donne, nous retournons alors une liste nulle.
Recherche un ensemble d'lments selon une mme valeur
Pour complter notre fonction de recherche, nous allons maintenant crer une fonction qui non pas s'arrte
au premier lment trouv, mais qui retourne tous les lments trouvs. Et devinez quoi ? Et bien oui, il
s'agit exactement du mme code que prcdemment, mais cette fois ci, sans l'utilisation de la variable found.
Ainsi, toute la liste est parcourue:
Code : C
1 Dlist *dlist_find_all(Dlist *p_list, int data)
2{
3
Dlist *ret = NULL;
4
if (p_list != NULL)
5
{
6
struct node *p_temp = p_list->p_head;
7
while (p_temp != NULL)
8
{
9
if (p_temp->data == data)
10
{
11
if (ret == NULL)
12
{
13
ret = dlist_new();
14
}
15
ret = dlist_append(ret, data);
16
}
17
p_temp = p_temp->p_next;
18
}
19
}
20
return ret;
21 }

Bon, je vous l'accorde, il y a quand mme une toute petite diffrence avec l'autre code. En effet, nous ne
pouvons pas crer notre liste de retour chaque fois que nous trouvons un lment. Il faut alors la crer ds
lorsque l'on aura trouv notre lment. Ensuite, nous nous contenterons d'ajouter tous les autres lments
13

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


trouvs, d'o l'intrt du
Code : C
1 if (ret == NULL)

Et bien voil, nous en avons maintenant termin avec nos fonctions de manipulation de listes chanes.
Passons maintenant un petit exercice pour vrifier vos acquis.

Exercice
Le but de cet exercice est de crer une fonction dlist_reverse permettant "d'inverser" une liste chane. Tous
les lments doivent alors tre inverss un par un (le premier doit se retrouver dernier, le deuxime avantdernier, ...). De plus, notre structure de liste nous donne une flexibilit incroyable car nous pouvons de
choisir de partir soit en dbut de liste, soit en fin.
Correction:
Code : C
1 Dlist *dlist_reverse(Dlist *p_list)
2{
3
Dlist *ret = NULL;
4
if (p_list != NULL)
5
{
6
struct node *p_temp = p_list->p_tail;
7
ret = dlist_new();
8
while (p_temp != NULL)
9
{
10
ret = dlist_append(ret, p_temp->data);
11
p_temp = p_temp->p_prev;
12
}
13
}
14
return ret;
15 }

Comme vous le voyez, j'ai choisi de partir en fin de liste puis d'ajouter en dbut de liste. Mais l'inverse est
aussi tout fait envisageable.

Algorithmes de quelques Oprations sur listes doublement chaines


Dclaration d'une liste doublement chane
type pointeur double = liste double :pointeur
liste double = structure
val: type lment
suivant, prcdent: pointeur double
fin stucture
type PtrD = listeD :pointeur
listeD = structure
val : entier
suiv, prec: PtrD
fin structure

Cration d'une liste doublement chane


Procdure creer_cellule(tete: PtrD)
var a: entier
dbut
nouveau(tete)
lire(a)
val(tete) a

14

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


prec(tete) nil
suiv(tete) nil
fin

Ajout d'un lment dans une liste doublement chane


Ajout en tete

procdure ajout_tete(tete: PtrD, v: entier)


var P: PtrD
dbut
nouveau(P)
val(P) v
prec(P) nil
suiv(P) tete
prec(tete) P
tete P
fin

Ajout avant P

procdure ajout_avant_P(tete: PtrD, v: entier)


debut
P1 tete
tant que P1 P faire
P1 suiv(P1)
fin tnat que
nouveau(P2)
val(P2) - v
suiv(P2) P
prec(P2) P^.prec
suiv(prec(P)) P2
prev(P) P2
fin

Ajout en fin de liste


15

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017

procdure ins_fin(tete: Ptr, v: entier)


var P: PtrD
dbut
P tete
tant que suivant(P) nil faire
P suivant(P)
fin tant que
nouveau(P1)
val(P1) v
prec(P1) P
suivant(P1) nil
suivant(P) - P1
fin

Suppression d'un lment dans une liste symtrique


Suppression en tte

procdure sup_tete(tete: PtrD)


var P: PtrD
dbut
P tete
si tete # nil alors
tete suivant(tete)
prec(tete) nil
liberer(P)
fin si
fin

Suppression la position P

16

Module : Algorithmique et structure de donnes - Anne universitaire : 2016-2017


Procdure Supp_P(tete: Ptr)
var P, P1: PtrD
dbut
P1 tete
tant que suivant(P1)P faire
P1 suivant(P1)
fin tant que
suivant(P1) suivant(P)
prec(suivant(P)) P1
librerer(P)
fin

Suppression en queue

procdure Sup_queue(tete: PtrD)


var P1: PtrD
dbut
P1 tete
tant que suivant(P1) nil faire
P1 suivant(P1)
fin tant que
suivant(prec(P1)) nil
liberer(P1)
fin

source :
http://www.mongosukulu.com/index.php/en/contenu/informatique-et-reseaux/algorithme/664-les-listesdoublements-chainees

17

Vous aimerez peut-être aussi