0% ont trouvé ce document utile (0 vote)
28 vues41 pages

Seance6 ALgo ENSAB Mode

Transféré par

Jawad Amin
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
28 vues41 pages

Seance6 ALgo ENSAB Mode

Transféré par

Jawad Amin
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
Vous êtes sur la page 1/ 41

LES LISTES DOUBLEMENT

CHAÎNÉES

Pr. Jaouad Danane


Université Hassan I
Ecole Nationale des Sciences Appliquées
de Berrechid
11. LES LISTES DOUBLEMENT
CHAÎNÉES

 DÉFINITION

 CONSTRUCTION DU PROTOTYPE D'UN ÉLÉMENT DE LA


LISTE

 OPÉRATIONS SUR LES LISTES


DOUBLEMENT CHAÎNÉES

2
1. DÉFINITION
 Les Listes Doublement Chaînées sont des structures de
données semblables aux listes simplement chaînées :

 L'allocation de la mémoire est faite au moment


de l'exécution

 La liaison entre les éléments se fait grâce à


deux pointeurs (un qui pointe vers l'élément
précédent et un qui pointe vers l'élément suivant)

 Le pointeur precedent du premier élément doit


pointer vers NULL (le début de la liste)

 Le pointeur suivant du dernier élément doit


pointer vers NULL (la fin de la liste)

3
1. DÉFINITION
 Pour accéder à un élément de la liste doublement
chaînée :

 en commençant avec la tête : le pointeur suivant


permettant le déplacement vers le prochain élément

 en commençant avec la queue : le pointeur precedent


permettant le déplacement vers l'élément précédent

1 2 7 5 3

tete queue

 La liste doublement chaînée peut être parcourue dans les


deux sens, du premier vers le dernier élément et/ou du
dernier vers le premier élément

4
2. CONSTRUCTION DU PROTOTYPE D'UN ÉLÉMENT
DE LA LISTE
 Pour définir un élément de la liste le type struct sera utilisé

 L'élément de la liste contiendra un champ donnee, un pointeur


precedent et un pointeur suivant

 Les pointeurs precedent et suivant doivent être du même type que


l'élément, sinon ils ne pourront pas pointer vers un élément de la liste

 Le pointeur precedent permettra l'accès vers l'élément précédent


tandis que le pointeur suivant permettra l'accès vers le prochain
élément

5
2. CONSTRUCTION DU PROTOTYPE D'UN ÉLÉMENT
DE LA LISTE
 Exemple 1 : représentation d’une liste de 5 éléments ‘A’,
‘B’,‘C’,‘D’ et ‘E’
typedef struct ElementListe {
char donnee ;
struct ElementListe *precedent ;
struct ElementListe *suivant ; } Liste;
 Pour avoir le contrôle de la liste il est préférable de
sauvegarder certains éléments : debut, fin, taille
 Le pointeur debut contiendra l'adresse du premier élément de
la liste. Liste *debut ;
 Le pointeur fin contiendra l'adresse du dernier élément de la
liste. Liste *fin ;
 La variable taille contient le nombre d'éléments. int taille ;

A B C D E
debut fin

6
3. OPÉRATIONS SUR LES LISTES DOUBLEMENT
CHAÎNÉES

 Nous allons travaillé par la suite avec les structures de


données et les déclarations suivantes :

typedef struct ElementListe {


char *info ;
struct ElementListe *precedent ;
struct ElementListe *suivant ; } Element;

int Taille ;
Element *Debut, *Fin;

7
3. OPÉRATIONS SUR LES L.D.C.
Initialisation

 Prototype de la fonction initialisation ( );

 Cette opération doit être faite avant toute autre opération


sur la liste

 Elle initialise le pointeur Debut et le pointeur Fin avec le


pointeur NULL, et la Taille avec la valeur 0

 La fonction

initialisation ( ) {
Debut = NULL;
Fin = NULL;
Taille = 0; }

8
3. OPÉRATIONS SUR LES L.D.C.
Insertion d'un élément dans la liste

 Algorithme d'insertion et de sauvegarde des éléments

• déclaration d'élément(s) à insérer


• allocation de la mémoire pour le nouvel élément
• remplir le contenu du champ de données
• mettre à jour les pointeurs vers l'élément précédent
et l'élément suivant
• mettre à jour les pointeurs vers le 1er et le dernier
élément si nécessaire
• Cas particulier : dans une liste avec un seul élément, le
1er est en même temps le dernier
• mettre à jour la taille de la liste

9
3. OPÉRATIONS SUR LES L.D.C.
Insertion d'un élément dans la liste

Pour ajouter un élément dans la liste il y a plusieurs


situations :

1. Insertion dans une liste vide

2. Insertion au début de la liste

3. Insertion à la fin de la liste

4. Insertion avant un élément

5. Insertion après un élément

10
3. OPÉRATIONS SUR LES L.D.C.
Insertion d'un élément dans la liste

1. Insertion dans une liste vide

Étapes :

• allocation de la mémoire pour le nouvel élément

• remplir les champs de données du nouvel élément

• le pointeur precedent du nouvel élément pointera vers NULL

• le pointeur suivant du nouvel élément pointera vers NULL

• les pointeurs Debut et Fin pointeront vers le nouvel élément

• la Taille est mise à jour

11
3. OPÉRATIONS SUR LES L.D.C.
Insertion d'un élément dans la liste

1. Insertion dans une liste vide


int ins_dans_liste_vide (char *info) {
Element *nou_element;
if ((nou_element = (Element*) malloc (sizeof(Element))) ==NULL)
return -1;
nou_element->info = (char *) malloc (50 * sizeof(char));
strcpy (nou_element-> info, info);
nou_element->precedent = NULL;
nou_element->suivant = NULL;
Debut = nou_element;
Fin = nou_element;
Taille++;
return 0;
}

12
3. OPÉRATIONS SUR LES L.D.C.
Insertion d'un élément dans la liste
2. Insertion au début de la liste
Étapes

• allocation de la mémoire pour le nouvel élément

• remplir le champ de données du nouvel élément

• le pointeur precedent du nouvel élément pointe vers NULL

• le pointeur suivant du nouvel élément pointe vers le 1er élément

• le pointeur precedent du 1er élément pointe vers le nouvel élément

• le pointeur Debut pointe vers le nouvel élément

• le pointeur Fin ne change pas

• la Taille est incrémentée

13
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste

2. Insertion au début de la liste


int ins_debut_liste (char *info) {
Element *nou_element;
if ((nou_element = (Element*) malloc (sizeof (Element))) ==NULL)
return -1;
nou_element->info = (char *) malloc (50 * sizeof(char));
strcpy (nou_element->info, info);
nou_element->precedent = NULL;
nou_element->suivant = Debut;
Debut->precedent = nou_element;
Debut = nou_element;
Taille++;
return 0;
}

14
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste

3. Insertion à la fin de la liste

Étapes :
• allocation de la mémoire pour le nouvel élément
• remplir le champ de données du nouvel élément
• le pointeur suivant du nouvel élément pointe vers NULL
• le pointeur precedent du nouvel élément pointe vers le dernier
élément (le pointeur Fin)
• le pointeur suivant du dernier élément va pointer vers le nouvel
élément
• le pointeur Fin pointe vers le nouvel élément
• le pointeur Debut ne change pas
• la Taille est incrémentée

15
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste

3. Insertion à la fin de la liste


int ins_fin_liste (char *info) {
Element *nou_element;
if ((nou_element = (Element*) malloc (sizeof (Element))) ==NULL)
return -1;
nou_element->info = (char *) malloc (50 * sizeof(char));
strcpy (nou_element->info, info);
nou_element->suivant = NULL;
nou_element->precedent = Fin;
Fin->suivant = nou_element;
Fin = nou_element;
Taille++;
return 0;
}

16
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste

4. Insertion avant un élément de la liste

L'insertion s'effectuera avant une certaine position passée en

argument à la fonction.

La position indiquée ne doit pas être le 1er élément. Dans ce cas il

faut utiliser les fonctions d'insertion au début de la liste.

17
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste
4. avant un élément de la liste
Étapes :
• allocation de la mémoire pour le nouvel élément
• remplir le champ de données du nouvel élément
• choisir une position dans la liste
• le pointeur suivant du nouvel élément pointe vers l'élément courant
• le pointeur precedent du nouvel élément pointe vers l'adresse sur la
quelle pointe le pointeur precedent d'élément courant
• si le pointeur precedent de l'élément courant est NULL alors le
pointeur Debut pointe vers le nouvel élément
• sinon le pointeur suivant de l'élément qui précède l'élément courant
pointera vers le nouvel élément
• le pointeur precedent d'élément courant pointe vers le nouvel élément
• le pointeurs Fin ne change pas
• la Taille est incrémentée d'une unité

18
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste
4. Insertion avant un élément de la liste
int ins_avant (char *info, int pos) {
int i;
Element *nou_element, *courant;
if ((nou_element = (Element*) malloc (sizeof (Element))) ==NULL)
return -1;
nou_element->info = (char *) malloc (50 * sizeof(char));
strcpy (nou_element->info, info);
courant = Debut;
for (i = 1; i < pos; ++i) courant = courant->suivant;
nou_element->suivant = courant;
nou_element-> precedent = courant->precedent;
if(courant->precedent == NULL) Debut = nou_element;
else courant->precedent->suivant = nou_element;
courant->precedent = nou_element;
Taille++;
return 0; }

19
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste
5. Insertion après un élément de la liste
• Étapes:
• allocation de la mémoire pour le nouvel élément
• remplir le champ de données du nouvel élément
• choisir une position dans la liste
• le pointeur suivant du nouvel élément pointe vers l'adresse sur la
quelle pointe le pointeur suivant d'élément courant
• le pointeur precedent du nouvel élément pointe vers l'élément
courant.
• si le pointeur suivant de l'élément courant est NULL alors le
pointeur Fin pointe vers le nouvel élément
• sinon le pointeur precedent de l'élément qui succède l'élément
courant pointera vers le nouvel élément
• le pointeur suivant d'élément courant pointe vers le nouvel élément
• la Taille est incrémentée d'une unité

20
3. OPÉRATIONS SUR LES L.D.C
Insertion d'un élément dans la liste

5. Insertion après un élément de la liste


int ins_apres (char *info, int pos){
int i;
Element *nou_element, *courant;
if ((nou_element = (Element*) malloc (sizeof (Element))) ==NULL)
return -1;
nou_element->info = (char *) malloc (50 * sizeof(char));
courant = Debut;
for (i = 1; i < pos; ++i) courant = courant->suivant;
nou_element->suivant = courant->suivant;
nou_element->precedent = courant;
if(courant->suivant == NULL) Fin = nou_element;
else courant->suivant->precedent = nou_element;
courant->suivant = nou_element;
Taille++;
return 0;
}

21
3. OPÉRATIONS SUR LES L.D.C
Suppression d'un élément dans la liste

 La suppression au début et à la fin de la L.D.C ainsi qu'avant ou après


un élément revient à la suppression à la position 1 ou à la position N
(nombre d'éléments de la liste) ou ailleurs dans la liste

 La suppression dans la L.D.C à n'importe quelle position ne pose pas


des problèmes grâce aux pointeurs precedent et suivant, qui
permettent de garder la liaison entre les éléments de la liste

 C'est la raison pour la quelle nous allons créer une seule fonction

 Si nous voulons supprimer :


• l'élément au début de la liste nous choisirons la position 1
• l'élément à la fin de la liste nous choisirons la position N
• un élément quelconque alors on choisit sa position dans la
liste

22
3. OPÉRATIONS SUR LES L.D.C
Suppression d'un élément dans la liste
Étapes:

• La position choisie est 1 (suppression du 1er élément de la liste)


• le pointeur supp_element contiendra l'adresse du 1er élément
• le pointeur Debut contiendra l'adresse contenue par le pointeur
suivant du 1er élément que nous voulons supprimer
• si ce pointeur vaut NULL alors nous mettons à jour le
pointeur Fin (liste avec un seul élément)
• sinon nous faisons pointer le pointeur precedent du 2ème
élément vers NULL)
• La position choisie est égale au nombre d'éléments de la liste
• le pointeur supp_element contiendra l'adresse du dernier élément
• nous faisons pointer le pointeur suivant de l'avant dernier
élément vers NULL
• nous mettons à jour le pointeur Fin

23
3. OPÉRATIONS SUR LES L.D.C
Suppression d'un élément dans la liste

Étapes:

• La position choisie est aléatoire dans la liste

• le pointeur supp_element contiendra l'adresse de l'élément à


supprimer

• le pointeur suivant de l'élément qui précède l'élément à


supprimer pointe vers l'adresse contenu par le pointeur suivant
d'élément à supprimer

• le pointeur precedent d'élément qui succède l'élément à


supprimer pointe vers l'adresse contenu par le pointeur
precedent d'élément à supprimer

• la Taille de la liste sera décrémentée d'un élément

24
3. OPÉRATIONS SUR LES L.D.C
Suppression d'un élément dans la liste

int supp(int pos) {


int i;
Element *supp_element,*courant;
if(Taille == 0) return -1;
if(pos == 1) { /* suppression de 1er élément */
supp_element = Debut;
Debut = Debut->suivant;
if(Debut == NULL) Fin = NULL;
else Debut->precedent = NULL;
}
else if(pos == Taille) { /* suppression du dernier élément */
supp_element = Fin;
Fin->precedent->suivant = NULL;
Fin = Fin->precedent;
}

25
3. OPÉRATIONS SUR LES L.D.C
Suppression d'un élément dans la liste

else { /* suppression ailleurs */


courant = Debut;
for(i=1;i<pos;++i) courant = courant->suivant;
supp_element = courant;
courant->precedent->suivant = courant->suivant;
courant->suivant->precedent = courant->precedent;
}
free(supp_element->info);
free(supp_element);
Taille--;
return 0;
}

26
3. OPÉRATIONS SUR LES L.D.C
Affichage de la liste

 Pour afficher la liste entière

• se positionner au début (Debut) de la liste ou à la fin


(Fin) de la liste

• parcourir la liste du 1er vers le dernier élément ou du


dernier vers le 1er élément en utilisant le pointeur suivant
ou precedent de chaque élément

• La condition d'arrêt est donnée par le pointeur suivant du


dernier élément qui vaut NULL ou le pointeur precedent du
1er élément qui vaut NULL

27
3. OPÉRATIONS SUR LES L.D.C
Affichage de la liste

 Pour afficher la liste entière

affiche() { /* affichage en avançant */


Element *courant;
courant = Debut; /* point du départ le 1er élément */
printf("[ ");
while(courant != NULL) {
printf("%s ", courant->info);
courant = courant->suivant;
}
printf("]\n");
}

28
3. OPÉRATIONS SUR LES L.D.C
Destruction de la liste

 Pour détruire la liste entière :


• On doit supprimer élément par élément
• la suppression peut être commencer par la position 1
tant que la Taille est plus grande que 0

La fonction
detruire () {
while (Taille > 0) supp(1);
}

29
4. EXERCICE
• L’objectif de ce problème est d’écrire un programme qui permet de
faire la somme de deux grands nombres entiers. Pour réaliser cet
objectif on doit lire une chaîne de caractères représentant un grand
nombre entier et le charger par paquets de 4 chiffres dans une liste
simplement chaînée.
• Pour définir un élément de la liste le type struct sera utilisé.
L'élément de la liste contiendra un champ info de type entier qui
représente les 4 chiffres du paquet et un pointeur suivant. Le
pointeur suivant est du même type que l'élément, sinon il ne
pourra pas pointer vers l'élément. Le pointeur suivant permettra
l'accès vers le prochain élément.

typedef struct ElementListe {


int info;
struct ElementListe *suivant; } Element;

30
4. EXERCICE
• Chaque nombre est donc représenté par une liste simplement
chaînée et pour avoir le contrôle de la liste, il est préférable de
sauvegarder les éléments suivants :
• Le pointeur debut contiendra l'adresse du premier élément de la
liste.
• Le pointeur fin contiendra l'adresse du dernier élément de la liste.
• La variable Npaquets contient le nombre d'éléments (nombre de
paquets).

• Pour simplifier l’écriture du programme et pour ne pas avoir
plusieurs variables globales, une autre structure de donnée doit
être ajouté (obligatoire) pour représenter les nombres à
additionner (Nombre1 et Nombre2) et le nombre somme
(Nombre3) :

31
4. EXERCICE
typedef struct Nombre {
Element *debut;
Element *fin;
int Npaquets; } GrandNombre;

• Les variables globales du programme peuvent être :

GrandNombre Nombre1, Nombre2, Nombre3 ;


char ch1[30], ch2[30];
/* ch1 chaîne qui représente le premier grand nombre*/
/* ch2 chaîne qui représente le deuxième grand nombre*/

32
4. EXERCICE
• 1- Ecrire la fonction qui permet d’initialiser les pointeurs Debut et Fin
à NULL et le Npaquets avec la valeur 0.

• Prototype de la fonction : initialisation(GrandNombre *Nb);

initialisation (GrandNombre *Nb) {


Nb->Npaquets=0;
Nb->Debut=NULL;
Nb->Fin=NULL;
}

33
4. EXERCICE
• 2- Ecrire la fonction qui permet d’insérer un élément dans une liste
vide

• Prototype de la fonction : int ins_liste_vide(GrandNombre *Nb, int info);

int ins_dans_liste_vide(GrandNombre *Nb,int i) {


Element *element;
if ((element = (Element *)malloc(sizeof(Element)))==NULL) return -1;
element-> info = i ;
element-> suivant = NULL;
Nb->Debut = element;
Nb->Fin = element;
Nb->Npaquets++;
return 0;
}

34
4. EXERCICE
• 3- Ecrire la fonction qui permet d’insérer un élément à la fin de la liste

• Prototype de la fonction : int ins_liste_fin(GrandNombre *Nb, int info);

int ins_fin_liste (GrandNombre *Nb,int i) {


Element *element;
if ((element = (Element *) malloc(sizeof(Element)))==NULL) return -1;
element-> info = i ;
element-> suivant = NULL;
Nb->Fin->suivant = element;
Nb->Fin = element;
Nb->Npaquets++;
return 0;
}

35
4. EXERCICE
• 4- Ecrire la fonction qui permet de charger une chaîne de caractères
par paquets de 4 chiffres dans la liste.
• Prototype de la fonction : convertir(GrandNombre *Nb, char *chaine);
Convertir(GrandNombre *Nb, char *chaine) {
int Long, reste, i, val, N, t;
char ch4[4], ch_reste[4],*ch;
Long= strlen(chaine); N=Long/4;
for (i=0; i<N; i++) {
val=Long - 4*(i+1); ch=&chaine[val]; strncpy(ch4, ch, 4); t=atoi(ch4);
if (Nb->Npaquets==0) ins_dans_liste_vide(Nb,t);
else ins_fin_liste (Nb,t); };
reste=Long%4;
if (reste!=0) {
ch=&chaine[0]; strncpy(ch_reste, ch, reste); t=atoi(ch_reste);
if (Nb->Npaquets==0) ins_dans_liste_vide(Nb,t);
else ins_fin_liste (Nb,t); }
}

36
4. EXERCICE
• 5- Ecrire la fonction qui permet de faire la somme de deux grands
nombres
• Prototype de la fonction : somme(GrandNombre Nb1, GrandNombre Nb2,
• GrandNombre *Nb3);
somme(GrandNombre Nb1, GrandNombre Nb2, GrandNombre *Nb3) {
int s, t, i, ret=0;
Element *element1, *element2 ;
element1=Nb1.Debut; element2=Nb2.Debut;
for (i=1; i<=Nb1.Npaquets;i++) {
s = element1->info + element2->info + ret;
t = s%10000;
if (Nb3->Npaquets==0) ins_dans_liste_vide(Nb3,t);
else ins_fin_liste (Nb3,t);
ret= s/10000;
element1=element1->suivant; element2=element2->suivant;
}; //fin de for

37
4. EXERCICE
• 5- Ecrire la fonction qui permet de faire la somme de deux grands
nombres
• Prototype de la fonction : somme(GrandNombre Nb1, GrandNombre Nb2, GrandNombre *Nb3);

for (i=Nb1.Npaquets+1; i<=Nb2.Npaquets; i++) {


s = element2->info + ret;
t = s%10000;
if (Nb3->Npaquets==0) ins_dans_liste_vide(Nb3,t);
else ins_fin_liste (Nb3,t);
ret= s/10000;
element2=element2->suivant;
}; //fin de for
if (ret !=0) ins_fin_liste(Nb3,ret);
}

38
4. EXERCICE
6- Ecrire la fonction qui permet d’afficher la liste entière à l’envers
Prototype de la fonction : afficher_envers(GrandNombre Nb);
afficher(GrandNombre Nb) {
char ch[4];
int i,s;
Element *element;
element=Nb.Debut;
sprintf(ch,"%d", element->info); printf("%s \n",ch);
for (i=2;i<=Nb.Npaquets; ++i) {
element=element->suivant; s=element->info;
if (s<=9) sprintf(ch,"000%d", s);
else if (s<=99) sprintf(ch,"00%d", s);
else if (s<=999) sprintf(ch,"0%d", s);
else sprintf(ch,"%d", s);
printf("%s \n",ch);
};
};

39
4. EXERCICE
6- Ecrire la fonction qui permet d’afficher la liste entière à l’envers
Prototype de la fonction : afficher_envers(GrandNombre Nb);

afficher_envers(GrandNombre Nb) {
Element *p, *q, *r;
p=Nb.Debut; q=p->suivant; r=q->suivant;
q->suivant=p;
while (r!= NULL) {
p=q; q=r;
r=r->suivant;
q->suivant=p;
}
Nb.Fin=Nb.Debut;
Nb.Debut=q;
afficher(Nb);
}

40
4. EXERCICE
7- Ecrire la fonction main() dont on trouve la lecture de deux grands
nombres (ch1 et ch2) et l’affichage du résultat de leurs somme

main(){
printf("Donner un très grand nombre : "); gets(ch1);
printf("Donner un très grand nombre : "); gets(ch2);
initialisation (&Nombre1); Convertir(&Nombre1, ch1);
initialisation (&Nombre2); Convertir(&Nombre2, ch2);
initialisation (&Nombre3);
if (Nombre1.Npaquets<=Nombre2.Npaquets) somme(Nombre1,
Nombre2, &Nombre3);
else somme(Nombre2, Nombre1, &Nombre3);
printf(" la somme = \n");
afficher_envers(Nombre3);
system("pause");
}

41

Vous aimerez peut-être aussi