Académique Documents
Professionnel Documents
Culture Documents
Avancées
Support du Cours
Driss El Ouadghiri
Email : dmelouad@gmail.com
Université My Ismail
Faculté des Sciences
Département de Mathématiques et d’Informatique
Meknès
Rappels :
• Structures (enregistrements)
• Pointeurs
• Allocation dynamique de la memoire
Tableau de structure
on déclare un tableau de structure de la même façon qu’un tableau de
variables simples :
struct adresse pers[50] ;
Attention: La structure adresse est déjà déclarée avant le tableau pers
qui est un tableau dont chaque élément est une structure de type adresse.
strcpy(rep_empl[0].nom ; ‘’Amalou’’) ;
strcpy(rep_empl[0].prenom ; ‘’Ider’’) ;
rep_empl[0].maison.numero = 19 ;
strcpy(rep_empl[0].maison.rue ; ‘’Avenue Zaid Ouhmad’’) ;
On peut accéder aux valeurs de ce tableau par :
char ch1;
ch1 = rep_empl[0].prenom ;
Exemple : la fonction suivante n’a pas d’effet lors de son appel depuis main
void permut(int a, int b) {
int c ;
c = a ; a = b ; b = c;
return ; }
Lors de l’appel de cette fonction depuis main, les valeurs des arguments
réels vont être copiés dans les variables de permut et ce sont ces variables
locales qui vont être modifiées, pas celle de main.
Ainsi, dans main, un appel de type permut(n, p) ; laissera n et p inchangés.
On dit que ces arguments sont passé par valeur.
D’une façon générale, on utilise des pointeurs avec les fonctions quand
on veut qu’une fonction modifie des variables du programme appelant.
Exemple :
int tab[6] ;
int pta, ptb ;
pta = tab ; ptb = pta + 2 ;
L’ordinateur a réservé en mémoire 6 fois par 4 octets pour le tableau tab
de trois entiers. La variable constante tab et donc pta contient l’adresse du
premier élément du tableau. L’opération ptb = pta + 5 n’ajoute pas 5 à la
valeur de pta, mais ajoute 5 fois le nombre d’octets correspondant à un int. Donc
ptb est n pointeur qui pointe sur le dernier élément du tableau. Il
contient donc l’adresse de tab[5].
Tableaux de pointeurs:
int tab[3] ;
int *ptab[3] = {tab, tab+1, tab+2} ; /* ptab est un tableau de 3 pointeurs
d’entiers */
Les valeurs de ptab sont des adresses de données. ptab est donc un
pointeur de pointeur car ptab pointe sur l’adresse de son 1er élément, qui
est lui même une adresse.
Driss EL Ouadghiri, FSMEK 13
dmelouad@gmail.com
Allocation dynamique de mémoire
La fonction malloc( )
Pour l’utiliser il faut inclure la bibliothèque <stdlib.h>.
malloc(n) ; renvoie l’adresse d’un bloc de mémoire de n octets libres ou la
valeur 0 s’il n’y a pas assez de mémoire.
int *p ;
p = malloc(50) ; /* fournit l’adresse d’un bloc de 50 octets libres */
/* et l’affecte au pointeur p */
Driss EL Ouadghiri, FSMEK 14
dmelouad@gmail.com
Utilisation de l’opérateur sizeof( ):
D’une machine à une autre, la taille réservée pour un type change. Nous
avons toujours besoin de la taille effective d’une donnée de ce type. C’est
l’opérateur sizeof qui nous fournit ce renseignement.
int a[8] ;
printf(‘’taille de a : %d \n’’, sizeof a) ;
printf(‘’taille de 4.52 : %d \n’’, sizeof 4.52) ;
printf(‘’taille de Bonjour! : %d \n’’, sizeof ‘’Bonjour!’’) ;
printf(‘’taille d’un float : %d \n’’, sizeof(float)) ;
Explication :
(a) un tableau de pointeur est un pointeur de pointeurs. On peut déclarer
au choix un tableau de pointeur : int *tab[6] ; ou un pointeur de pointeur : int
**tab ;. Dans le cas de l’allocation dynamique de mémoire, comme on ne
connaît pas la taille de du tableau dont on aura besoin, on déclare un ointeur
de pointeurs.
Exemple1 :
/* Fonction qui affiche une matrice quelconque */
void aff_mat(double **A,int l,int c)
{
int i,j;
for(i=0; i<l; i++){
for(j=0; j<c; j++) { printf("%lf ",A[i][j]); }
printf("\n"); }
printf("\n"); }
Driss EL Ouadghiri, FSMEK 18
dmelouad@gmail.com
Exemple2:
Structures de Données
Avancées
1) Définitions et Exemples
Une pile est une liste linéaire dont une seule extrémité ( le
sommet ) est accessible ( Visible).
put push
sommet
Exemples : Pile d’assiettes.
Pile de dossiers.
2) Caractéristiques
L’extraction ou dépilement puis l’ajout se font uniquement
au sommet de la pile.
Une pile est en théorie un objet dynamique (en opposition
à un tableau qui est statique)
Driss EL Ouadghiri, FSMEK 24
dmelouad@gmail.com
Représentation statique :
pile creer(){
pile p;
p.sommet = -1;
return p;
}
Driss EL Ouadghiri, FSMEK 27
dmelouad@gmail.com
Tester si une pile est vide ou non :
Dans ce problème :
[ {x+(y-[ ( {x+(y-[a+b])*c-[(
( {x+(y-[a+b]) [
{ { {
Pile vide
{x+(y-[a+b])*c-[(d+e)
[ Expression
{ complète
Ainsi l’expression est correcte.
Driss EL Ouadghiri, FSMEK 32
dmelouad@gmail.com
Remarque :
const max = 20 ;
var p : pile ;
s, symb : char ;
valid : boolean ;
sommet, pos : integer ;
expr : string[80] ;
valide := true;
initialise ;
pos:=1;
readln(expr);
symb := expr[pos];
write(‘expression incorrecte’)
readln;
Remarque:
Deux modes de récursivité :
• Direct : la fonction fait appel à elle même d’une façon directe.
• Indirect ( croisé ) : la fonction fait référence à une procédure
ou une fonction qui lui fait référence.
type F ( arguments )
debut
initialisation ;
appel à F avec une condition d’arrêt ;
instruction ;
Fin Driss EL Ouadghiri, FSMEK 40
dmelouad@gmail.com
Exemple : Factoriel
n! = n*(n-1)!
0! = 1 ( condition d’arrêt )
Les appels des fonctions récursives sont en fait empilées dans une pile
système. Une fonction de ce type possède donc deux parcours: la phase
de descente et la phase de remontée.
n == 3 n*2 == 6
n == 2 n*1 == 2
n == 1 n == 1
Condition de sortie : n == 1
1
Empilement des 2 2
valeurs de n 1 2 6
3 3 3
4 fact 4 fact 4 fact
24
4
fact
fact(4)
• Débordement de pile (Stack Overflow) : Ceci l’une des causes les plus
souvent rencontrés dans le plantage de programmes avec des fonctions
récursives. En effet, les appels récursifs de fonctions sont placés dans la pile
du programme. Cette pile est d'une taille assez limité car elle est fixée une fois
pour toutes lors de la compilation.
Dans la pile sont non seulement stockés les valeurs des variables de
retour mais aussi les adresses des fonctions. les données sont nombreuses et
un débordement de la pile peut très vite arriver ce qui provoque des sorties
anormales du programme.
Driss EL Ouadghiri, FSMEK 45
dmelouad@gmail.com
Qu’on rencontre souvent le problème de « ‘Stack Overflow’, une approche
itérative sera préférable qu’une approche récursive. L’approche récursive
demande beaucoup de moyens en ressources alors l’approche itérative, telle
une boucle for, est bien moins coûteuse en terme de ressources et est bien
plus sûre, sauf dans le cas d'un dépassement de capacité bien sûr.
Remarque :
Exemple :
File d’attente devant un bus, devant un guichet automatique, etc …
Une file suit la discipline FIFO (First In, First Out), le premier arrivé est
Le premier servi.
• un tableau
• indice du premier élément
• indice du dernier élément
Insertion de C Extraction de A
C ar C ar
B ar B
av B av
A av A
file créer_file_vide(){
file f ;
f.av = 0 ;
f.ar = -1 ;
return f ;
}
c = pf->tab[0] ;
for( i = 0 ; i < ar-1 ; i++) {
pf->tab[i] = pf->tab[i+1] ; }
ar := ar-1 ;
Exemple :
E ar E E E E
D D D av D av D av
C av C av = 3 H ar
G ar G
F ar = 1 F ar F F
Insère F extrait C Insère G Insère H
Les files et les piles sont des listes particulières, elles sont ordonnées
linéairement. La représentation séquentielle, dans la mémoire, préservait
cet ordre.
Inconvénients :
1) Overflow
2) Capacité non utilisée au maximum
Supposons que l’ordre est gérer par le programmeur. Pour chaque élément
de la liste on resserve un champs qui contient l’adresse de l’élément suivant
(pointeur).
Liste • •
info suivant Dernier nœud
Liste : est un pointeur externe qui nous permet d’accéder à la liste.
Liste = 2000
Driss EL Ouadghiri, FSMEK 60
dmelouad@gmail.com
Manipulation des listes chaînées
Structure de donnée définissant un nœud :
int recher_liste-rec(elt x) {
noeud *a;
a = liste;
if (a == NULL) { return 0; }
else if (a->contenu == x) { return 1; }
else { return (recher_liste-rec(x)); } /* appel récursif */
}
Driss EL Ouadghiri, FSMEK 64
dmelouad@gmail.com
Longueur d’une liste :
Y0 Y1 Y2
z0z1z2 z0z1z2 z0z1z2
coeff xi yj zk suivant
Tête queue
poly
. Poly 1 0 1 1
.
. Poly 1 0 2 0 1 0 1 1
y2 yz
Initialisation :
Poly = NULL;
Poly = (noeud *)malloc (sizeof (noeud));
liste fin
Consiste à passer en revue tous les nœuds de la liste et à effectuer sur chacun
d’eux une action spécifique :
void enumeration() {
noeud *p ;
p = (noeud *)malloc (sizeof (noeud));
p = liste ;
while (p != NULL ) {
action(p) ;
p = p->suivant ;
}
return;
}
noeud *recherche(info x) {
int trouver ; /* booléen*/
noeud *p;
p = liste ; trouver = 0 ;
while ((p != NULL) and (trouver == 0)) {
if ( p->info = x ) { trouver = 1 ; }
else { p = p->suivant ; }
} /* fin while */
return(p);
}
Remarque :
Cette fonction recherche la première occurrence de x, renvoie un
pointeur p sur le nœud trouvé ou NULL sinon.
void retrait1(info x) {
noeud *p, *pred ;
int trouve ;
if (liste == NULL ) { printf(‘‘liste vide’’) ; }
else if (liste->info = x) { /* x se trouve dans le premier nœud */
p = liste ; liste = p->suivant ; free(p) ;
}
else { /* x se trouve au milieu ou à la fin de la liste */
pred = liste ; p = pred->suivant ; trouve = 0 ;
}
Driss EL Ouadghiri, FSMEK 78
dmelouad@gmail.com
while (p != NULL) and ( trouve == 0 ) {
if (p->info = x) { trouve = 1 ; }
else {
pred = p ; p = p->suivant ;
}
} /* fin de while */
if (p == NULL ) { printf(‘’Information inexistante’’) ;}
else {
pred->suivant = p->suivant ; free(p) ;
}
return;
}
return;
}
Remarque : Dans le Programme principal, si p == NULL ce n’est pas la
peine d’appeler la procédure, si non retrait2(p); free(p) ;
20 0 2 11 0 5 02 0 1 01 1 1
2- Écrire la fonction d’insertion d’un terme en fin de la liste, pointé par un pointeur de
tête liste.
3- Écrire la fonction d’insertion d’un terme en fin de la liste, pointé par un pointeur de
tête liste et un pointeur de fin fin.
4- Écrire la fonction d’insertion d’un terme dans la liste, pointé par un pointeur de tête
liste, en conservant l’ordre de la liste.
Liste quelconques : L A B C R
L A B C R
Driss EL Ouadghiri, FSMEK 84
dmelouad@gmail.com
void insert_ch( nœud *N ; info x) {
/*insertion d’une information x avant un nœud d’adresse N*/
noeud *pred , *p ;
p = (noeud *)malloc (sizeof (noeud));
p->info = x ;
if ( R == NULL ) and (L == NULL ) {
L=p;
R=p;
p->D = NULL ;
p->G = NULL ;
}
else if (L==N) {
/*insérer avant le premier nœud*/
p->G = NULL; L = p ; p->D = N ; N->G = p ;
}
else {
pred = N->G ; pred->D = p ; p->G = pred ;
N->G = p ; p->D = N ;
}
}
Driss EL Ouadghiri, FSMEK 85
dmelouad@gmail.com
2- Les listes circulaires
• Le dernier nœud contient un pointeur sur le premier plutôt
que la valeur NULL.
• les listes circulaires permettent comme les listes à double
chaînage d’accéder au prédécesseur d’un nœud donné.
• Elles permettent aussi de simplifier les opérations d’insertion
et de suppression.
Remarque :
1) Le test de liste vide sera éliminé par l’adjonction d’un nœud
en tête.
P(x) = 3x5 + 7x2 + 2x
? -1 3 5 7 2 2 1
liste
? -1 Polynôme vide
Driss EL Ouadghiri, FSMEK 86
dmelouad@gmail.com
2) Le cas des nœuds d’extrémité sera éliminé (implantation en liste
circulaire)
P
Les opérations deviennent :
tête X
IIII A B C
pred N
Insertion :
{
pred = N->G ;
pred->D = p ;
p->G = pred ;
p->D = N ;
N->G = p ;
}
Driss EL Ouadghiri, FSMEK 87
dmelouad@gmail.com
Retrait d’un nœud d’adresse M :
(On ne peut pas supprimer le nœud d’entête)
if (M != tete) {
pred = M->G ; succ = M->D ;
pred->D = succ ;
succ->G = pred ;
free(M) ;
}
else printf(‘’Suppression du nœud d’entête’’) ;
I- Les graphes
Un graphe fini est un ensemble de points appelés sommets (nœuds) et
d’arrêtes reliant ces sommets.
Exemple :
- Réseau Routier
Sommets : nœuds du réseau (croisements, rond-points)
Arrêtes : axes de communication.
- Graphe d’ordonnancement qui exprime les contraintes d’antériorité
entres les différentes taches d’un projet.
Driss EL Ouadghiri, FSMEK 89
dmelouad@gmail.com
toiture façade
plomberie
B F B
F
Fils D
E D
E D
C
- Nœud terminal (feuille) : tout nœud qui n’as pas de fils gauche et de fils droit.
- les arbres binaires
Déclaration :
typedef struct nœud {
nœud *G ;
type info ;
noeud *D ;
}
l 5 l 6 9
d
e 6 a 7 8
e re 7 ge 0 0
a u
r 8 nt 0 0
ge nt r vre 9 e 10 11
10 r 0 0
11 u 12 13
coude, coudre, coulage, coulant,
12 r 0 0
couler, couleur, couleuvre
13 vre 0 0