Vous êtes sur la page 1sur 86

Notions de structures de

données dynamiques

Les fichiers, l’allocation dynamique, les listes, …

1
Généralités sur les
structures de données dynamiques
• Ce sont des organisations de données qui,
au cours de l’exécution d’un programme,
voient la taille de l’espace qui leur est alloué
en mémoire (centrale ou secondaire)
évoluée en relation directe avec les besoins
de l’utilisateur.
• Elles sont différentes des SD statiques qui
gardent la même taille d’espace au cours de
l’exécution du programme, quels que soient
les besoins exprimés par l’utilisateur.
2
Généralités sur les
structures de données dynamiques
• Font partie de cette famille de
structures de données :
–les fichiers
–les tableaux alloués dynamiquement
–les listes
–les files
–les piles
– …
3
Les fichiers

4
Généralités sur les fichiers
Les mémoires de masse des ordinateurs (disques durs)
permettent aujourd’hui de stocker des quantités d’informations
gigantesques. Une certaine organisation est nécessaire pour
retrouver facilement les objets qui nous intéressent. Tous les
systèmes de gestion de fichiers utilisent aujourd’hui la notion
d’arborescence des répertoires (MSDOS, WINDOWS 95,
WINDOWS NT, UNIX, LINUX, cartes à puce SIM, …)

5
C:

Windows etu-mp MSVC

mp1 mp2

mp1a mp1b mp1c mp1d

martin

tp1.cpp
tp2.doc fichiers
dupont

tp1.cpp
cv.doc

répertoires
6
Un fichier est un ensemble d’informations cohérentes.
On distingue les fichiers de données (résultats d’un
calcul, d’une expérience ....), les fichiers programmes
(.cpp, .bas, .pas ......), les fichiers textes (.txt), les fichiers
exécutables (.exe, .com, .bat)

Ecrire un programme prog.cpp qui analyse des données


contenues dans un fichier (texte) EXP.DTA

• où se situe le fichier de données ?


• existe-t-il (est-ce le bon nom ?)
• quelle est sa nature? (numérique, texte ..?)
• que veut-on en faire (modifications, traitements ...?)

7
Stocker les données dans un fichier séparé du programme
satisfait aux exigences de modularité et
d’indépendance. Cette stratégie nous conduit donc à
une meilleure conception de nos programmes.

La gestion du format du fichier estUnà lafichier


chargeBMP
du
programmeur. Il faut donc se documenter sur le format du
fichier (fichiers ASCII, fichiers graphiques …) pour
pouvoir le traiter dans le programme.

8
Le fichier xns.txt

Le fichier xnsin.xls

9
Exemple d'écriture dans un
fichier

On veut ranger 12 nombres réels aléatoires


dans un fichier nommé Data.txt

10
int main(int argc, char * argv[]) #include <fstream.h>
{ #include <cstdlib>
fstream f;
long i;
f.open("data.txt",ios::out);
for(i=0;i<12;i++)
{
f<<alea()<<endl;
}
f.close();
return 0;
}

double alea()
{
return 100*rand()/(RAND_MAX+1.0);
}
11
Insérer ici une animation faisant
Cas du pointeur de fichier qui se déplace
Au fur et à mesure que des opérations
Se font dans le fichier.

12
Exemple de lecture dans un
fichier (1)

On veut relire les 12 nombres réels aléatoires


du fichier data.txt et en faire la moyenne.

13
int main(int argc, char * argv[]) #include <iostream.h>
{ #include <fstream.h>
fstream f;
long i;
double x, som=0;
f.open("data.txt",ios::in);
for(i=0;i<12;i++)
{
f>>x;
som=som+x;
}
f.close();
cout<<"Moyenne: "<<som/12<<endl<<endl;
return 0;
}

14
Exemple de lecture dans un
fichier (2)

On veut relire les 12 nombres réels aléatoires


du fichier data.txt et les mettre dans un
tableau.

15
#include <fstream.h>
#include <iostream.h>
void AfficheTableau(double t[], long dim)
{
int main(int argc, char * argv[])
long i;
{
for(i=0;i<dim;i++)
fstream f;
{
long i;
cout<<t[i]<<endl;
double t[12];
}
f.open("data.txt",ios::in);
return;
for(i=0;i<12;i++)
}
{
f>>t[i];
}
f.close();
AfficheTableau(t,12);
return 0;
}

16
#include <iostream.h>
#include <fstream.h>

void AfficheTableau(double t[], long dim);


void RemplirTableau (fstream f,double t[], long dim)
void RemplirTableau(fstream { f,double t[], long dim);
long i;
for(i=0;i<dim;i++)
{
int main(int argc, char * argv[]) f>>t[i] ;
{ }
fstream toto; return;
double titi[12]; }
toto.open("data.txt",ios::in);
RemplirTableau(toto,titi,12);
toto.close();
AfficheTableau(titi,12);
return 0;
17
}
void AfficheTableau(double t[], long dim);

void RemplirTableau (char *NomF,double t[], long dim);

void RemplirTableau (char *NomF,double t[], long dim)


{
fstream f;
long i;
f.open(NomF,ios::in);
for(i=0;i<dim;i++)
{
f>>t[i] ;
}
int main(int argc, char * argv[])
f.close();
{ return;
double t[12]; }
RemplirTableau ("data.txt",t,12);
AfficheTableau(t,12);
return 0;
} 18
Vérifier l'existence du fichier
#include <iostream.h>
#include <fstream.h>

int main(int argc, char * argv[])


{
fstream f;
f.open("data.txt",ios::out);
if (f.bad()){
cout<<"Ouverture impossible";
exit(8);
} Un problème avec le fichier ?
……
}
19
#include <iostream.h>
#include <fstream.h>
Attention aux fins de fichiers
const unsigned MAX=80;

int main(int argc, char * argv[])


{
char ligne[MAX];
fstream f;
f.open("blabla.txt",ios::in);
while( !f.eof() ){
f.getline(ligne, MAX);
cout<<ligne<<endl;
}
f.close();
return 0;
}
20
Dans le fichier DATA.txt , on a les données suivantes:

34 2 /* ligne 1*/

123.78 2.5 /* ligne 2*/

452.65 8.52 /* ligne 3*/

...... ...... ......

633.57 4.85 /* ligne 34*/

856.24 8.95 /* ligne 35*/

Il y a Il
34ypoints
a 35 lignes dans le fichier
d’expérience, chaque!!!!
point a 2
coordonnées
21
Le travail à faire

• créer un tableau de 2*50 réels


• ouvrir le fichier (existe-t-il?)
• lire la première ligne et vérifier que les dimensions sont
cohérentes
• lire successivement chacune des lignes et stocker les
données dans le tableau
• fermer le fichier

22
#include <iostream.h>
#include <fstream.h>

int main(int argc, char * argv[])


{
double T[50][2];
int nbligne,nbcolonne;
fstream f;
f.open("data.txt",ios::in);
if (f.bad())
exit(0); Le fichier existe-t-il ?
f>>nbligne>>nbcolonne;
if((nbcolonne != 2)||(nbligne>50))
exit(0); Est-il à la norme ?
RemplirTableau(f,T,nbligne);
f.close();
return 0;
23
}
void RemplirTableau (fstream f,double t[50][2], int n)
{
int i;
for(i=0;i<n;i++)
{ T 0 1
f>>t[i][0]>>t[i][1] ;
0
}
return ; 1
}

48 24
49
Fichiers et structures

struct date{ struct etudiant{


long jour; char nom[20];
long mois; char prenom[20];
long an; date dnaissance;
}; char sexe;
};

Dupont Emilie Martin Jean


Née le 19/12/1979 Né le 12/10/1984
25
Dupont Emilie
Dupont Emilie
Dupont Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née
Martin Jean le 19/12/1979
Née le 19/12/1979
Née Dupont
le 19/12/1979
Emilie
Né le 12/10/1984 DupontEmilie
Dupont
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née le 19/12/1979
Dupont Emilie Née le 19/12/1979
Née Dupont
le 19/12/1979
Emilie
Dupont Emilie
Née le 19/12/1979 Dupont Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Dupont
le 19/12/1979
Emilie
Née Aubert
le 19/12/1979
Julien
Née le 19/12/1979
Née le 19/12/1979
Né le 1/8/1979

26
Le travail à faire

• Écrire une fonction de saisie des coordonnées d'un


étudiant
• Écrire une fonction d'affichage des coordonnées d'un
étudiant
• Écrire une fonction de sauvegarde des coordonnées d'un
étudiant dans un fichier
• Écrire une fonction de lecture des coordonnées d'un
étudiant dans un fichier

27
Les prototypes

void saisie(etudiant *x);


void affiche(etudiant x);
void EcrireFichierEtudiant(fstream f, etudiant x);
void LireFichierEtudiant(fstream f, etudiant *x);

28
int main(int argc, char * argv[])
{
etudiant a,b;
fstream f;
saisie(&a);
affiche(a);
f.open("Etudiants.txt",ios::out);
EcrireFichierEtudiant(f,a);
f.close();
f.open("Etudiants.txt",ios::in);
LireFichierEtudiant(f,&b);
f.close();
affiche(b);
return 0; 29
}
Le code des fonctions
void saisie(etudiant *x)
{
cout<<endl<<"Nom Prenom : ";
cin>>x->nom>>x->prenom;
cout<<endl<<"Sexe (M ou F) : ";
cin>>x->sexe;
cout<<endl<<"date de naissance (jj mm aa) : ";
cin>>x->dnaissance.jour>>x->dnaissance.mois>>x->dnaissance.an;
return;
}
30
void affiche(etudiant x)
{
cout<<endl<<x.nom<<" "<<x.prenom<<"\t";
if (x.sexe=='M'){
cout<<"ne le " <<x.dnaissance.jour<<"/";
cout<<x.dnaissance.mois<<"/";
cout<<x.dnaissance.an;
}
else {
cout<<"nee le " <<x.dnaissance.jour<<"/";
cout<<x.dnaissance.mois<<"/";
cout<<x.dnaissance.an<<endl;
}
return;
31
}
void EcrireFichierEtudiant(fstream f, etudiant x)
{
f<<x.nom<<"\t"<<x.prenom<<endl;
f<<x.sexe<<endl;
f<<x.dnaissance.jour<<"\t";
f<<x.dnaissance.mois<<"\t";
f<<x.dnaissance.an<<endl;
return;
}

32
void LireFichierEtudiant(fstream f, etudiant *x)
{
f>>x->nom>>x->prenom;
f>>x->sexe;
f>>x->dnaissance.jour;
f>>x->dnaissance.mois;
f>>x->dnaissance.an;
return;
}

33
Saisir les coordonnées de 10 étudiants et les
sauvegarder dans un fichier
int main(int argc, char * argv[])
{
etudiant t[10];
int i;
fstream f;
for(i=0;i<10;i++)
{
saisie(&t[i]);
}
f.open("Etudiants.txt",ios::out);
for(i=0;i<10;i++)
{
EcrireFichierEtudiant(f,t[i]);
}
f.close();
return 0; 34
}
Lire les coordonnées de 10 étudiants à partir
d'un fichier

int main(int argc, char * argv[])


{
etudiant t[10];
int i;
fstream f;
f.open("Etudiants.txt",ios::in);
for(i=0;i<10;i++)
{
LireFichierEtudiant(f,&t[i]);
}
f.close();
return 0;
}

35
Les flots
Un flot est un canal recevant ou fournissant des informations. Un flot d'entrée
est un objet de type istream tandis qu'un flot de sortie est un objet de type
ostream.

Le flot cout est un flot prédéfini (en tant qu'objet de la classe


ostream_withassign, classe dérivée de la classe ostream) connecté à la sortie
standard stdout (l'écran en général), tandis que le flot cin est connecté à l'entrée
standard stdin (le clavier en général).

Du point de vue du langage C (et même du C++), l'entrée et la sortie standards


sont des fichiers particuliers. On retrouvera donc les mêmes techniques pour
gérer les fichiers en général.
/* The predefined streams */
extern istream_withassign _RTLENTRY _EXPDATA cin;
extern ostream_withassign _RTLENTRY _EXPDATA cout;
extern ostream_withassign _RTLENTRY _EXPDATA cerr;
extern ostream_withassign _RTLENTRY _EXPDATA clog; 36
Statut d'erreur d'un flot
A chaque flot est associé un ensemble d'indicateurs de statut (bits
d'erreurs). La classe ios (dont dérivent ostream et istream) définit
les constantes suivantes :
• eofbit le flot n'a plus de caractères disponibles (fin
de fichier)
• failbit la prochaine opération sur le flot ne pourra
pas aboutir
• badbit le flot est dans un état irrécupérable
• goodbit
La (lorsqu'il
classe ios contient vaut 0) aucune
5 fonctions des erreurs
membres précédentes
pour accéder à ces
indicateurs:
•eof() valeur de eofbit
• bad() valeur de badbit
• fail() valeur de failbit
• good() valeur de goodbit
• rdstate() valeur du statut d'erreur

On peut repositionner (à la main) les valeurs de ces indicateurs (en


37
utilisant la fonction membre clear().
Les indicateurs de mode
d'ouverture d'un flot
Ils indiquent les actions à opérer dès l'ouverture du flot (ils sont
surtout associés aux manipulations de fichiers).

Ces indicateurs font partie de l'énumération open_mode

• in ouverture du flot en mode lecture


• out ouverture du flot en mode écriture
• ate positionnement en fin de fichier à l'ouverture
• app ajout en fin de fichier
• trunc
• nocreate
• noreplace
• binary

38
Allocation dynamique

39
#include <iostream.h>
#include <stdlib.h>

#define N 10

void init( int tab[ ],long dim);


Allocation statique
int main(int argc, char * argv[]) d’un tableau
{
int t[N];
init(t,N); Le programmeur
return 0; décide (avant l’exécution)
}
void init( int tab[ ],long dim) de la taille du tableau
{
long i;
for(i=0;i<dim;i++)
{ Mais cette fonction marche
tab[i] =rand(); avec un tableau de
} dimension quelconque
return; 40
}
#include <iostream.h>
#include <stdlib.h>

Demande à l’exécution
void init( int tab[ ],long dim);
de la dimension du
int main(int argc, char * argv[]) tableau
{
long p;
int t[]; passage du tableau
cin >> p; et de sa dimension
init(t,p); à la fonction
….
}

void init( int tab[ ],long dim)


{
idem
}
41
Pointeurs

Soit V une variable représentant une certaine donnée. Le compilateur


lui alloue automatiquement de la mémoire.

double *p; double V;

p &V V 10

On
Onpeut
p contient
peutalors accéder
l’adresse
affecter à la de
l’adressevariable
deVVà pV
*p désigne
par l’intermédiaire
lap=&V;
valeur pointée
de ppar p

42
#include <iostream.h>
#include <fstream.h>
Illustration
int main(int argc, char * argv[])
{
int V;
int * p;
fstream toto;

p=&V;
cout<<"Donner la valeur de V :"; Fourniture
cin>>V; de la valeur
toto.open("c:\\pointeurs.txt",ios::out); 30
toto<<"La valeur &V vaut \t:"<<&V<<endl;
toto<<"La valeur &p vaut \t:"<<&p<<endl;
toto<<"La valeur de V vaut \t:"<<V<<endl;
toto<<"La valeur *p vaut \t:"<<*p<<endl;
toto<<"La valeur p vaut \t:"<<p<<endl;
toto.close();
return 0; 43
}
t[4] 0x0012FF7C
t[3] 0x0012FF78
t[2] 0x0012FF74
t[1] 0x0012FF70
t[0] 0x0012FF6C

nn 0x0012FF68
n 0x0012FF64
ptr_dentier 0x0012FF60
ptr_dcar 0x0012FF5C
mot 0x0012FF48
Exemples d’allocation mémoire 44
Illustration du contenu du fichier généré
par le programme précédent.

La valeur &V vaut :0x0012FF70


La valeur &p vaut :0x0012FF6C
La valeur de V vaut :13
La valeur *p vaut :13
La valeur p vaut :0x0012FF70

45
L'opérateur new
L'opérateur new retourne dynamiquement (à l’exécution)
un pointeur sur un bloc de mémoire capable de contenir le
nombre d'objets de type T choisi par le programmeur.

#include <iostream.h>
#include <stdlib.h>

int main(int argc, char * argv[]) int * t;


{
int t[ ];
int p;
cin >> p; Donnez moi l’espace
t=new int[p]; pour ranger p entiers
….. dans un tableau nommé t.
} 46
int * t;
t=new int[5];
cout<<" valeur de l’adresse du 1er elt du tableau : "<<t;
cout<<" valeur de l’adresse du 2e elt du tableau : "<<&t[1];

*(t+1) ?= t[1]

1er entier 2e entier 3e entier


t[0] t[1] t[2]

47
int * P; double * Q; Allocation d’un espace
P=new int; constitué de nbligne éléments,
Q=new double; chaque élément étant un
pointeur vers un objet de type
int **t; int
cin>>nbligne; int[nbcolon]
cin>>nbcolon;
t=new int *[nbligne];  *t
*t=new int[nbcolon];
t
 
48
int *
0 65537
Donnez moi la place 1
pour ranger
un tableau de 50 étudiants

windows

Word

windows
struct etudiant{
char nom[20];
char prenom[20];
date date_naissance;

Internet
char sexe;

Visual C++
};

65536 131072

49
struct etudiant{
char nom[20];
char prenom[20];
date
date_naissance;

windows
char sexe;
};
#include <iostream.h>
#include <stdlib.h>
t 165897
165897
int main(int argc, char *
argv[])
{
etudiant *t;
int p;
cin >> p; On veut
t=new etudiant[p]; un tableau de
….. 50 étudiants 50
}
Il n'y a plus
de place!!

windows
#include <iostream.h>
#include <stdlib.h> t NULL

int main(int argc, char * argv[])


{
etudiant *t;
int p;
cin >> p; On veut
t=new etudiant[p]; un tableau de
….. 50 étudiants 51
}
Tester le résultat de l'appel à
l'opérateur new
#include <iostream.h>
#include <stdlib.h>

windows
int main(int argc, char * argv[])
{
etudiant *t; t
int p;
cin >> p;
t=new etudiant[p]; On veut
if (t!=NULL){ un tableau de
…. 50 étudiants
}
….
}
52
#include <iostream.h>
#include <stdlib.h>

int main(int argc, char * argv[])


{
double *t;
long p;
cin >> p;
t=new double[p];
if(t != NULL)
{
for(i=0;i<p;i++)
{
t[i]=rand()/(RAND_MAX+1.0);
}
}
else
{
cout<<" Erreur d’allocation"; exit(1);
}
….. 53
}
int main(int argc, char * argv[])
{
etudiant statique[100];
etudiant * dynamique;
fstream fichier;
int p;

fichier.open("c:\\titi.txt", ios::out);
if(!fichier.fail())
{

fichier<<"adresse de debut du tableau statique \t="<<&statique[0]<<endl;


fichier<<"adresse de fin du tableau statique \t="<<&statique[99]<<endl;
fichier<<"taille en bytes du tableau statique \t="<<sizeof(statique)<<endl;

cout<<"Donner la taille du tableau dynamique : "; 10


cin >> p;
dynamique=new etudiant[p];
if (dynamique !=NULL)
{
fichier<<"Le systeme vient de m'allouer mon bloc !!!"<<endl;
fichier<<"adresse de debut du tableau dynamique \t="<<&dynamique[0]<<endl;
fichier<<"adresse de fin du tableau dynamique \t="<<&dynamique[p-1]<<endl;
fichier<<"taille en bytes du tableau dynamique \t="<<
int(&dynamique[p-1]) - int(&dynamique[0])+sizeof(etudiant)<<endl;
}

fichier.close();
} 54
return 0;
}
Résultat obtenu lors de l’exécution du
Programme précédent (contenu du fichier c:\titi.txt)

adresse de debut du tableau statique = 0x0012E994


adresse de fin du tableau statique = 0x0012FF3C
taille en bytes du tableau statique = 5600
Le systeme vient de m'allouer mon bloc !!!
adresse de debut du tableau dynamique = 0x004416C0
adresse de fin du tableau dynamique = 0x004418B8
taille en bytes du tableau dynamique = 560

55
Allocation dynamique d’une matrice
Une matrice est un tableau de tableaux
int ** t;
tt est un tableau dont chaque élément est un
pointeur sur entier
t[0]
t[1]
t[1][2]
t[2] int * * t;
t[3]

56
cin>>nbligne;
cin>>nbcolonne;
t= new int *[nbligne];
for(i=0;i<nbligne;i++)
{
t[i]=new int[nbcolonne];
}

57
Allocation dynamique et fonction
Une fonction peut retourner un objet
qui a été alloué dynamiquement
(dans la mesure où l’allocation a été possible)

#include<iostream.h>
#include<stdlib.h>

int alea(int n);


void init(int *t, int dim);
void affiche(int *t, int dim);
int * somme(int * t1, int * t2, int dim);

58
int * somme(int * t1, int * t2, int int main(int argc, char * argv[])
dim) {
{ int * T;
int i; int * U;
int * res; int dim=10;
res=new int[dim];
for(i=0;i<dim;i++) T=new int[dim];
{ U=new int[dim];
res[i]=t1[i]+t2[i];
} init(T,dim);
return res; init(U,dim);
} affiche(T,dim);
cout<<endl<<"+"<<endl;
affiche(U,dim);
Somme de cout<<endl<<"="<<endl;
affiche(somme(T,U,dim),dim);
deux return 0;

tableaux }
59
T 12 3 5 9 30 4 1 15

+
U 0 18 7 100 24 0 -2 18

=
12 21 12 109 54 4 -1 33

somme
60
L'opérateur delete
L'opérateur delete effectue l’opération inverse de new,
en ce sens qu’il libère (restitue au système) l’espace
initialement alloué dynamiquement. Cet opérateur ne doit
être appliqué qu’aux pointeurs issus d’un new effectué
par le programme.
#include <iostream.h>
#include <stdlib.h>
L’espace de p entiers
int main(int argc, char * argv[])
{ initialement alloué est
int t[ ]; rendu au système (désalloué).
int p;
cin >> p;
t=new int[p]; Le pointeur t existe mais ne pointe
….. sur rien, et du coup t[0] et t[i] n’ont
delete t; aucun sens
} 61
L'opérateur delete
Attention à la désallocation d’objets n’ayant pas été créés
par l’emploi de l’opérateur new dans le programme !!! Les
conséquences sont imprévisibles et ce type d’erreur
difficilement repérable.
#include <iostream.h>
#include <stdlib.h>

int main(int argc, char * argv[])


{
int i=10;
int * p;
p=&i;
delete p;
….. 62
}
int main(int argc, char * argv[])
{ int t[]; int valeur; int *ptr;
t[0]=2; t[1]=3; t[2]=100;
/* cout<<"Les valeurs du tableau sont
: "<<t[0]<< " ; "<<t[1]<<" ;
"<<t[2]<<endl;
ptr=&t[1];
valeur=*ptr;
cout<<endl<<"la valeur de t[1] vaut :
"<<valeur<<endl; */
return 0; 63

}
Listes

64
Généralités sur les listes
• Les listes sont des structures de données
correspondant à un chaînage d’objets de
même type, une chaîne dont la taille
(nombre d’éléments de la liste) varie en
fonction des besoins de l’utilisateur
• A la différence d’un tableau, les éléments de
la liste ne sont pas forcément contigüs
• Chaque élément d’une liste (maillon)
renferme des données et des informations
de gestion (pointeurs) lui permettant de
faire partie de la chaîne
65
Généralités sur les listes
• Les pointeurs sont pour chaque maillon
le moyen de raccordement au reste de
la liste  plusieurs sortes de listes :
listes simplement chaînées
listes doublement chaînées
• Le pointeur de tête de liste indique le
premier élément de la liste
• La queue de liste ou dernier maillon
pointe par convention sur NULL
66
Listes simplement chaînées
• Ces listes ne peuvent être parcourues
que de la tête vers la queue de liste

Pointeur
Données vers le
suivant Pointeur
Tête de liste Données vers le
suivant

Queue de liste

Données NIL

67
Listes simplement chaînées
Il s’agit de chaîner des maillons d’allure suivante
créés dynamiquement :
struct maillon {
// Déclaration des
// champs de
// données
maillon * ptrsuiv ; // pointeur vers
// le maillon suivant
}
68
Exemple de déclaration de maillon
d’une liste simplement chaînée

struct etudiant {
char nom[20];
char prenom[30];
long matricule;
etudiant * suivetu ;
}

69
Listes doublement chaînées
• Ces listes peuvent être parcourues
dans les deux sens
Tête/queue de liste
Pointeur Pointeur Pointeur
NIL Données avant arrière Données avant

Pointeur
arrière Données NIL

Queue/tête de liste 70
Listes doublement chaînées
Il s’agit de chaîner des maillons d’allure suivante
créés dynamiquement :
struct maillon {
// Déclaration des
// champs de
// données
maillon * ptr_suiv ;
maillon * ptr_pred ;
}
71
Exemple de déclaration de maillon
d’une liste doublement chaînée
struct livre {
char titre[120];
char cote[20];
char auteur[80];
char motscles[40];
livre * suivlivre ;
livre * predlivre ;
}
72
Généralités sur les listes
• Les principales opérations sur une liste sont :
créer une liste
parcourir une liste à la recherche d’une
valeur donnée
ajouter (début, fin, à n’importe quelle
position) un maillon à une liste existante
supprimer un maillon d’une liste
modifier les données d’un maillon donné
d’une liste
ordonner ou trier une liste
73
Files

74
Généralités sur les files
• Les files sont des listes simplement chaînées
particulières sur lesquelles les opérations
d’E/S ne peuvent se faire qu’à des endroits
précis :
 Une entrée (ajout) d’élément s’effectue
toujours en queue de file
 Une sortie (suppression) d’élément se fait
toujours en tête de file
 A chaque instant doivent être connus
l’élément en tête de file et
l’élément en queue de file
75
Généralités sur les files

76
Exemple de file
Pointeur Pointeur
Données vers le Données vers le
suivant suivant
Queue de liste

Pointeur Pointeur
Données vers le Données vers le
suivant suivant

Tête de liste

Données NIL

77
Piles

78
Généralités sur les piles (1)
• Une pile est un tableau d’objets d’un type donné
alloué dynamiquement et sur lequel les opérations
d’E/S ne peuvent se faire qu’à un endroit précis : le
haut/sommet de pile.
• La pile est caractérisée par : un pointeur de bas ou
fond de pile, une profondeur maximale de pile, et
un pointeur de sommet ou haut de pile.
• Une entrée (empilement) d’élément ou une sortie
(dépilement) d’élément se fait toujours en haut de
pile.
 A tout instant sont connus les
pointeurs de bas et de haut de pile
79
Généralités sur les piles (2)
• L’empilement se fait par convention vers les
adresses basses.
• Le dépilement se fait par convention vers les
adresses hautes.
• Lorsque la profondeur maximale de la pile est
atteinte on dit que la pile est pleine. La tentative de
réalisation dans ces conditions d’un empilement 
débordement de pile par le haut.
• Lorsque le pointeur de sommet de pile coïncide
avec le pointeur de bas pile on dit que la pile est
vide. La tentative de réalisation dans ces conditions
d’un dépilement  débordement de pile par le bas.

80
Exemple de pile
Adresse de début
du tableau

Profondeur ou hauteur
maximale de la pile
Sommet de pile 3e objet empilé
après réalisation
de 3 empilements
2e objet empilé

1er objet empilé

Bas de pile (adresse


plus haute du tableau) 81
Arbres binaires

82
Généralités sur les arbres binaires
• Un arbre binaire est une liste d’objets d’un type
particulier alloué dynamiquement et dont chaque
élément appelé NOEUD peut avoir au maximum
deux nœuds fils auxquels il est relié.
• Dans un tel arbre, il existe :
 un unique nœud n’étant pas fils d’aucun autre
nœud : c’est la RACINE de l’arbre ;
 des nœuds n’ayant aucun nœud fils : ce sont les
FEUILLES de l’arbre.
• Le parcours d’un tel arbre se fait généralement de
la racine vers les feuilles.
• La hauteur de l’arbre = le plus long chemin (suite
de pointeurs) séparant la racine des feuilles.
83
Exemple d’arbre binaire
Nœud A Racine de l’arbre

C=Fils droit de A
B=Fils gauche de A

E=Fils droit de C
NULL NULL D=Fils gauche de C

F=Fils gauche de E NULL


NULL NULL

B, D et F sont des feuilles


NULL NULL 84
Autres arbres

• Un arbre binaire peut être balancé ou équilibré i.e


avoir la hauteur la plus petite possible.
• Un arbre peut être quelconque i.e avoir plus de
deux fils.
• Plusieurs arbres créent une forêt.

85
FIN

86

Vous aimerez peut-être aussi