Les adresses et les pointeurs - PowerPoint PPT Presentation

1 / 131
About This Presentation
Title:

Les adresses et les pointeurs

Description:

C'est par cette adresse que le processeur peut communiquer ... Incr menter une adresse ne veux pas dire ajouter 1, cela veut dire aller l'adresse suivant la variable courante. ... – PowerPoint PPT presentation

Number of Views:89
Avg rating:3.0/5.0
Slides: 132
Provided by: uqac
Category:

less

Transcript and Presenter's Notes

Title: Les adresses et les pointeurs


1
Les adresses et les pointeurs
2
Les adresses
Les cases mémoires ont toutes un numéro qui les
distingue les une des autres ce numéro est
appelé adresse. Cest par cette adresse que le
processeur peut communiquer avec la mémoire.
0
1
2
3
4
. . .
. . .
max
3
Les adresses et les variables
Le nom que lon donne au cases mémoires est
traduit en une adresse juste avant lexécution
dun programme. Cela est nécessaire afin que le
processeur sache à quelle case mémoire est
associée chaque variable. En général il est
impossible de prévoir à quelle adresse sera
placée une variable. Le nom des variables est
donc nécessaire.
0
c1 1
char
2
c1 c2 Lire le contenu de la case 3. Mettre ce
qui a été lu dans la case 1.
c2 3
char
1324
4
. . .
. . .
max
4
Le partage de la mémoire
Sur les systèmes modernes, il peut y avoir
plusieurs usagers se partageant la mémoire et
chaque usager peut exécuter plusieurs programmes
simultanément. Cela signifie que lon nest pas
libre dutiliser toutes les cases mémoires comme
on le veut. Une case peut être occupée par un
programme à un certain moment et libre à un
autre. Cette situation est aléatoire. Pour cette
raison, on ne mentionne jamais explicitement une
adresse dans un programme même si cela est
théoriquement possible.
5
Adresses valides et non valides
Exemple. Dans le pseudo-code suivant Lire le
contenu de la case 3. Mettre ce qui a été lu
dans la case 1. Que se passe t-il si au moment
de lexécution la case mémoire 1 est déja
utilisée par un autre programme. La case est
alors non valide et il y aura erreur à
lexécution. Cest pour cette raison que lon
utilise des variables. Avant lexécution, une
adresse valide est associée à chaque variable.
Seul notre programme pourra utiliser ces cases
mémoire.
6
Position des variables dans la mémoire
a
int
. . .
. . .
Sauf pour les tableaux, il ny a aucune garantie
que les variables occupent des cases adjacentes
en mémoire. Exemple. int a,b4,c,d3

int
b0
int
b1
int
b2
int
b3
int
c
. . .
. . .
d0
int
int
d1
int
d2
. . .
. . .
7
Les adresses et les types
Une des principales fonctions des types est
dindiquer le nombre doctets utilisés par une
variable. Par exemple nous avons vu que un
caractère prend 1 octet (8 bits) un entier prend
4 octets (32 bits). Cela signifie que si on
divise la mémoire en case dun octet alors un
char utilise 1 case un int utilise 4 cases
adjacentes
n 0
00000000
int
1
00000000
2
00000000
3
00001101
c1 4
char
00011011
. . .
c2 5
00011100
char
. . .
. . .
max
8
Remarque
On peut aussi voir la mémoire comme une suite de
cases de taille variable.
n 0
int
00000000000000000000000000001101
int
c1 4
char
00011011
. . .
c2 5
00011100
char
. . .
. . .
max
9
Les adresses et les tableaux
Le nom dun tableau correspond à ladresse du
début du tableau. Exemple char
tab5 printf(p\n, tab) 4027630992
printf(p\n, tab1) 4027630993 printf(
p\n, tab2) 4027630994 Note p sert
à afficher les pointeurs.
10
Les tableaux dentiers
Exemple int tab5 printf(p\n, tab)
4027630976 printf(p\n, tab1)
4027630980 printf(p\n, tab2)
4027630984 Question Pourquoi?
4
4
11
Lincrémentation dune adresse
a 16216
Ladresse a1 nest pas valide
int
. . .
. . .
int
b0 b24600
b1 b124604
int
int
b2 b224608
Incrémenter une adresse ne veux pas dire ajouter
1, cela veut dire aller à ladresse suivant la
variable courante. En général cela na du sens
que si on est dans un tableau.
int
b3 b324612
. . .
. . .
d0 d54316
char
char
d1 d154317
char
d2 d254318
. . .
. . .
12
Remarque
Si Tab est un tableau alors Ladresse de Tab0
est Tab, ladresse de Tab1 est Tab
1, ladresse de Tab2 est Tab 2, etc. Cela
est vrai quelque soit le type des éléments de Tab.
13
Lopérateur
Il est possible de connaître, pendant lexécution
dun programme, ladresse associée à une
variable. En C, cela est possible à laide de
lopérateur unaire
Exemple char c int n, tab1000 Ladresse
de c est c Ladresse de n est n Ladresse de
tab3 est tab3
14
Lopérateur
Il est aussi possible de connaître, pendant
lexécution dun programme, le contenu de la case
mémoire située à une adresse donnée. En C, cela
est possible à laide de lopérateur unaire
  • Exemple
  • char c
  • int tab1000
  • Le contenu de ladresse tab 25 est (tab 25)
  • (tab 25) est donc identique à tab25
  • (c) est identique à c
  • c na aucun sens

15
Exemple
  • Les expressions logiques suivantes sont vraies
  • n 12556
  • (12560) 60
  • (12560) lt c2
  • (r) 12.345

. . .
. . .
int
5000000
n 12556
c1 12560
char
60
char
c2 12561
61
double
r 12562
12.345
. . .
. . .
16
Résumé des opérations sur les adresses
  • On peut
  • Additionner une adresse et un entier
  • Déterminer ladresse dune variable
  • Déterminer le contenu dune adresse
  • Mais, on ne peut pas
  • Additionner deux adresses
  • (mais on peut soustraire deux adresses dun
    même tableau)

17
Les pointeurs
Un pointeur est une variable pouvant contenir une
adresse.
Exemple int pn pointeur sur une valeur
entière char pc pointeur sur un
caractère double pr pointeur sur un réel
18
Les pointeurs exemple
pn 12556
int
Exemple int pn, m
. . .
. . .
m 65710
int
. . .
. . .
19
Les pointeurs exemple
65710
pn 12556
int
Exemple int pn, m pn m
. . .
. . .
m 65710
int
. . .
. . .
20
Les pointeurs exemple
65710
pn 12556
int
Exemple int pn, m pn m m 6
. . .
. . .
6
m 65710
int
. . .
. . .
21
Les pointeurs exemple
65710
pn 12556
int
Exemple int pn, m pn m m 6 pn
38
. . .
. . .
38
m 65710
int
. . .
. . .
22
Les pointeurs et les tableaux
En C les pointeurs sont intimement liés aux
tableaux. Exemple int tab10,
p ptab tab3 70 (tab 3)
70 p3 70 (p 3) 70
tous équivalents
23
Remarque
Le nom dun tableau est une adresse constante et
non pas un pointeur qui est une
variable. Exemple int tab10, p ptab p
tab / Valide / tab p / Non valide /
0
1
2
3
4
5
6
7
8
9
10
tab
p
24
Quelques utilités des pointeurs
  • Pour implanter le passage de paramètres par
    référence
  • Pour implanter le passage de tableaux en
    paramètre
  • Pour utiliser des indices négatifs au tableaux
  • Fondamental en structure de données

25
Les pointeurs comme paramètres
En C echanger(a, b)
void echanger( int x, int y) int tmp tmp
x x y y tmp
26
Les pointeurs comme paramètres
En C echanger(a, b)
void echanger( int x, int y) int tmp tmp
x x y y tmp
27
Les tableaux passés en paramètre
Une des plus importantes utilisation des
pointeurs réside dans le passage des tableaux en
paramètre. Lorsque lon passe le nom dun
tableau en paramètre, on passe une adresse. La
fonction appelée reçoit donc une adresse quelle
met dans une variable cette variable doit donc
être un pointeur.
28
Les tableaux passés en paramètre
void liretab(int tab , int max) int c
int i0 while ((cgetchar()) ! EOF)
tabi c i i 1
29
Les tableaux passés en paramètre
void liretab(int tab, int max) int c int
i0 while ((cgetchar()) ! EOF)
(tabi) c i i 1
30
Les indices de tableaux
Exemple 1 Tableau dentiers dont les indices
vont de -5 à 5 int tab11 int ptab ptab
tab 5 ptab0 est identique à tab5 ptab-5
est identique à tab0 ptab5 est identique à
tab10
0
1
2
3
4
5
6
7
8
9
10
tab
ptab
31
Les indices de tableaux
Exemple 2 Tableau dentiers dont les indices
vont de A à Z int tab26 int
ptab ptab tab - A / A vaut 65 en ASCII
/ ptabA est identique à tab0 ptabZ
est identique à tab25
0
1
2
3
4
21
22
23
24
25
-65
tab
?
ptab
32
Allocation statique et dynamique
Jusquà maintenant, toutes la mémoire que nous
avons utilisée dans nos programmes devait avoir
été allouée avant l'exécution à laide des
déclarations de variables. Il est parfois utile
dallouer une partie de lespace mémoire en cours
dexécution.
33
Exemple 1
Par exemple, si on a besoin de mémoriser un
certain nombre dobjets mais que ce nombre nest
pas connu avant lexécution du programme. Il
faut alors allouer suffisamment despace au cas
où le nombre dobjets est grand. Si le nombre
dobjets est petit, on gaspille inutilement de
lespace mémoire.
34
Le fichier dentête stdlib.h
Le fichier dentête stdlib.h contient des
déclarations de fonctions traitant, entre autres,
de lallocation de la mémoire - malloc -
free - calloc - realloc
35
(void )malloc(size_t size)
size_t est un type dentiers positifs. malloc
retourne un pointeur sur un espace mémoire
réservé à un objet de taille size, ou bien NULL
si cette demande ne peut être satisfaite. La
mémoire allouée nest pas initialisée. Bien
entendu, quand on travaille avec des pointeurs en
C, ces derniers possédent chacun un type. Par
conséquent, le type de données (void ) retourné
par malloc doit toujours être mis dans le type de
donnée avec lequel nous voulons travailler..
36
Exemple 2
int p p 10 / INVALIDE puisque p ne pointe
sur / / aucune case mémoire valide
/ p (int) malloc(sizeof(int)) p
10 / VALIDE /
p
Avant malloc
p
Après malloc
37
Exemple 2
int p p 10 / INVALIDE puisque p ne pointe
sur / / aucune case mémoire valide
/ p (int) malloc(sizeof(int)) p
10 / VALIDE /
Pourquoi?
p
Avant malloc
p
Après malloc
38
Pointeurs sur void
La fonction malloc ne sait pas à quoi servira
lespace mémoire qui lui est demandé. Elle ne
sait pas quel type dobjet utilisera cet
espace. Alors, elle retourne un pointeur
générique qui peut être converti en nimporte
quel type de pointeur un pointeur sur void
39
Conversion implicite
En C, certaines conversions de type sont
implicites double x int n char c n
c / un char est converti en un int / c n
/ un int est converti en un char / x n
/ un int est converti en un double / n x
/ un double est converti en un int /
40
Conversion explicite
Dans toute expression, on peut forcer
explicitement des conversions de types grâce à un
opérateur unaire appelé cast. Dans la
construction (nom de type) expression lexpressio
n est convertie dans le type précisé (selon
certaines règles).
41
Exemple 3
printf(d, pow(2,3)) / mauvaise façon
/ printf(d, (int) pow(2,3)) / bonne façon
/ int p struct complexe cplx p (int )
malloc(sizeof(int)) cplx (struct complexe )
malloc(sizeof(struct complexe))
42
void free(void p)
free libère lespace mémoire pointé par p elle
ne fait rien si p vaut NULL. p doit être un
pointeur sur un espace mémoire alloué par malloc,
calloc ou realloc.
43
void calloc(size_t nobj, size_t size)
calloc retourne un pointeur sur un espace mémoire
réservé à un tableau de nobj objets, tous de
taille size, ou bien NULL si cette demande ne
peut pas être satisfaite. La mémoire allouée
est initialisée par des zéros.
44
void realloc(void p, size_t size)
realloc change en size la taille de lobjet
pointé par p. Si la nouvelle taille est plus
petite que lancienne, seul le début du contenu
de lobjet est conservé. Si la nouvelle taille
est plus grande, le contenu de lobjet est
conservé, et lespace mémoire supplémentaire
nest pas initialisé. realloc retourne un
pointeur sur un nouvel espace mémoire, ou bien
NULL si cette demande ne peut pas être
satisfaite, auquel cas p nest pas modifié.
45
Exemple 4
On veut lire des entiers et les mettre en
mémoire. Plutôt que de créer un tableau avant
lexécution, on utilise calloc pendant
lexécution. int p, n scanf(d, n) p
(int ) calloc(n, sizeof(int)) si plus tard cet
espace nest plus suffisant, alors on utilise p
(int ) realloc(p, 2n)

p
46
Exemple 5
Liste chaînée struct noeud int valeur
noeud suivant struct noeud chaine, p
chaine
p
47
Exemple 5
chaine (struct noeud) malloc(sizeof(struct
noeud))
valeur
suivant
chaine
p
48
Exemple 5
chaine (struct noeud) malloc(sizeof(struct
noeud)) chaine -gt valeur 8
8
chaine
p
49
Exemple 5
chaine -gt suivant (struct noeud)
malloc(sizeof(struct noeud))
8
chaine
p
50
Exemple 5
p chaine -gt suivant
8
chaine
p
51
Exemple 5
p -gt valeur 5
8
5
chaine
p
52
Exemple 5
p -gt suivant (struct noeud) malloc(sizeof(struct
noeud))
8
5
chaine
p
53
Exemple 5
p p -gt suivant
8
5
chaine
p
54
Exemple 5
p -gt valeur 13 p -gt suivant NULL
8
5
13
0
chaine
p
55
  • Nous venons ainsi de créer une liste de valeurs
    qui sont liées entre elles par des pointeurs.
    Autrement dit, ces valeurs en mémoire peuvent
    être dans des endroits non contigues. Pou accéder
    ces valeurs, il suffit davoir ladresse de la
    première valeur qui est dans le pointeur chaine.

56
Impression des valeurs dun chaine
  • Soit une liste chainée en mémoire centrale, dont
    le premier élément est à ladresse chaine.
  • Question Imprimer toutes les valeurs constiuant
    cette liste.

57
Exemple 6
p chaine while (p ! NULL) printf(d\n,
p-gtvaleur) p p-gtsuivant
8
5
13
0
chaine
p
58
Exemple 6
  • La solution précédente consiste à mettre
    ladresse du premier élément dans le pointeur p.
    La valeur p-gtvaleur est alors affichée pour
    aller à lélément suivant, il suffit davoir son
    adresse qui est dans
  • p-gtsuivant. Ce processus est répété jusquà
    atteindre l fin de la chaine qui est donnée par
    le pointeur NULL.

59
Suppression dun élément dans une liste chainée
  • Soit une liste chainée en mémoire centrale, dont
    le premier élément est à ladresse chaine.
  • Question Supprimer lélément de valeur X sil
    existe.

60
  • Solution Dans un premier temps, nous devons
    rehercher cette valeur dans la liste. Si elle
    nexiste pas, alors il ny a rien à faire.
  • Maintenant, si elle existe. Nous devons avoir
    ladresse de son emplacement en mémoire
    (supposons que cette adresse soit donnée par p),
    et ensuite distinguer les deux cas suivants

61
Lélément à supprimer nest pas le premier de la
liste
Q-gtSuivant P-gtsuivant free(P)
8
5
13
0
chaine
P
Q
8
5
13
0
chaine
Lélément à supprimer est le le premier de la
liste
Chaine chaine-gtsuivant Free(P)
P
62
  • Si cette valeur est en première position de la
    liste. Dans ce cas, en la supprimant, le début de
    cette liste va changer. Autrement dit, cest le
    deuxième élément qui va devenir le premier
    élément. Ceci sexprime par linstruction
    suivante
  • chaine p-gtsuivant ou alors par
  • chaine chaine-gtsuivant

63
  • Maintenant, si cette valeur nest pas le premier
    élément, alors on doit mettre dans lélément
    précédent cette valeur ladresse de lélément
    suivant cette valeur. Si Q est ladresse de
    lélément qui précéde cette valeur, alors cette
    idée est exprimée comme suit
  • Q-gtsuivant p-gtsuivant
  • Ensuite, on supprime physiquement de la liste
    lemplacement de cette valeur pour la rendre
    disponible pour deventuels appels dallocation
    mémoire ultérieurs. Ceci est fait par
  • free(p)

64
Lalgorithme est alors comme suit
  • p chaine trouve 0
  • while ((p ! NULL) (!trouve))
  • if (p-gtvaleur x)
  • trouve 1
  • else
  • q p / sauvegarde ladresse
    actuelle
  • avant daller à lélément
    suivant
  • p p-gtsuivant
  • If (trouve) / lélément existe/
  • if (p chaine) / cet élément est le
    premier de la liste
  • chaine p-gtsuivant
  • else q-gtsuivant p-gtsuivant
  • free(p)
  • else printf(élément inexistant)

65
Ajout un élément X dans une liste chaînée
  • Linsertion dun élément peut se faire de deux
    manières
  • 1. Après un élément donné de valeur V
  • 2. Avant un élément donné de valeur V

66
  • 1. Après un élément de valeur V
  • La solution consiste dans un premier temps à
    chercher si cet élément de valeur V existe dans
    la liste. Si cet élément nexiste pas, il ny a
    rien à faire. Sinon, on procède comme suit
  • - soit P ladresse de cet élément
  • - allouer un espace mémoire pour ce nouvel
    élément à laide de la fonction malloc soit Q
    ladresse de cet espace mémoire
  • - après insertion, lélément X sera entre
    lélément de valeur V et le suivant de V.
    Autrment dit, le suivant de V sera maintenant le
    nouvel élément X et le suivant de X sera le
    suivant de V de tantôt. En termes algoritmiques,
    on exprime cette idée comme suit

67
  • Q.suivant P-gtsuivant (mettre comme suivant de X
    le suivant de V
  • P.suivant Q (mettre comme suivant de V
    lélément X

8
V
13
0
chaine
X
P
Q
68
Lalgorithme est alors comme suit
  • p chaine trouve 0
  • while ((p ! NULL) (!trouve))
  • if (p-gtvaleur V)
  • trouve 1
  • else p p-gtsuivant
  • If (trouve) / lélément existe /
  • Q (struct noeud) malloc(sizeof (struct
    noeud))/ alloue une addresse mémoire
  • Q-gtvaleur X / y mettre la valeur X /
  • Q-gt suivant p-gtsuivant
  • P-gtsuivant Q
  • else printf(élément inexistant)

69
  • 2. Insertion avant un élément de valeur V
  • La solution consiste dans un premier temps à
    chercher si cet élément de valeur V existe dans
    la liste. Si cet élément nexiste pas, il ny a
    rien à faire. Sinon, on procède comme suit
  • - soit P ladresse de cet élément
  • - allouer un espace mémoire pour ce nouvel
    élément à laide de la fonction malloc soit Q
    ladresse de cet espace mémoire.
  • - après insertion, lélément X sera entre
    lélément de valeur V et le lélément précédent
    de V. Autrment dit, le précédent de V aura pour
    élément suivant lélément X et le précédent de V
    sera maintenant le nouvel élément X. Pour
    effectuer ces opérations, on aura besoin de
    ladresse de lélément précédent de V. Soit
    preced cette adresse. Notons que si V est le
    premier élément, alors après insertion de X, ce
    sera X qui sera le nouveau premier élément de la
    liste.
  • En termes algoritmiques, on exprime cette idée
    comme suit

70
  • Si p chaine (signifie que V est le premier
    élément de la liste)
  • linsertion va se faire comme suit
  • Q-gtsuivant chaine (le suivant de
    X est V)
  • chaine Q (X devient le premier
    élément de la liste chaînée)
  • Sinon (V nest pas le premier élément de la
    liste)
  • precedent-gtsuivant Q (le suivant
    du précédent de V est X)
  • Q-gtsuivant p (le suivant de X
    est V)

71
Lalgorithme est alors comme suit
  • p chaine / initialiser p au début de la liste
    /
  • trouve 0
  • while ((p ! NULL) (!trouve))
  • if (p-gtvaleur x)
  • trouve 1 / élément trouvé /
  • else preced p
  • p p-gtsuivant
  • If (trouve) / lélément existe
  • Q (struct noeud) malloc(sizeof (struct
    noeud))/ alloue une addresse mémoire
  • Q-gtvaleur X
  • if (chaine p) / élément V est en
    première position de la liste/
  • Q-gtsuivant chaine / (le suivant de
    X est V) /
  • chaine Q / (X devient le
    premier élément de la liste chaînée)/
  • else
  • Q-gtvaleur X / mettre la valeur
    X dans la case mémoire référencée par Q /
  • preced-gt suivant Q / mettre le
    suivant du précédent de V lélément X /

72
Lélément V nest pas le le premier de la
liste Precedent-gtsuivant Q Q-gtsuivant P
8
V
13
0
chaine
X
P
preced
Q
5
13
0
chaine
V
X
Lélément V est le le premier de la liste
Q-gtsuivant chaine Chaine Q
P
Q
73
Avantages des pointeurs sur les tableaux
  • La suppression et lajout dun élément se font
    sans déplacer les autres éléments comme dans les
    tableaux
  • Le nombre déléments na pas besoin dêtre connu
    à lavance.
  • La longeur de la liste nest pas figée (peut
    augmenter ou diminuer)
  • Mais, demande plus despace mémoire que le
    tableaux.

74
Listes doublement chainées
  • Une liste est dite doublement chainée si chaque
    élément contient ladresse de lélément suivant
    et celle de lélément précédent.

chaine
Null
75
  • La déclaration de cette structure de données est
    comme suit

Liste doublement chaînée typedef struct
listelement int valeur struct listelement
suivant struct listelement preced
noeud noeud chaine, p
76
Création de la liste
  • typedef fin -1
  • noeud creation(void)
  • / construit la liste dentiers jusquà
    lecture de fin
  • int donnee
  • noeud nouveaupoint, debut, derriere
  • / construit le premier élément, sil y en a
    un /
  • scanf(d,donnee)
  • if (datafin)
  • debut NULL
  • else debut (noeud ) malloc(sizeof
    (noeud))
  • debut-gt valeur donnee
  • debut-gtsuivant NULL
  • debut-gtpreced NULL
  • derriere debut
  • / continuer la création /
  • for (scanf (d, donnee) donnee
    ! fin scanf (d, donnee))
  • nouveaupoint (noeud )
    malloc(sizeof (noeud)) / alloue de la mémoire
    /
  • nouveaupoint-gtvaleur
    donnee / remlissage de la mémoire/

77
Impression de la liste
  • void impression (noeud debut)
  • noeud pointeur
  • pointeur debut
  • while (pointeur ! NULL)
  • printf(d\n, pointeur-gtvaleur)
  • pointeur pointeur-gtsuivant

78
Ajout dune valeur avant une autre
  • void ajouteravant(noeud debut, int valeur, int
    data)
  • / ajouter dun élément valeur avant
    lélément data sil existe /
  • noeud pointeur, debut, nouveau, avant
  • pointeur debut trouve 0
  • while ((nouveaupoint !NULL) (!trouve))
  • if pointeur-gtvaleur data
  • trouve 1
  • else pointeur pointeur-gtsuivant / aller à
    lélément suivant/
  • if (trouve) / élément existe /
  • nouveaupoint (noeud ) malloc(sizeof
    (noeud)) / alloue de la mémoire pour le nouvel
    élément/
  • nouveaupoint-gtvaleur valeur
  • / tester maintenant si lélément est le
    premier de la liste
  • if (pointeur debut)
  • nouveaupoint-gtsuivant debut
  • nouveaupoint-gtpreced NULL
  • debut nouveaupoint

79
Ajout dune valeur après une autre
  • void ajouterapres(noeud debut, int valeur, int
    data)
  • / ajouter lélément valeur avant lélément
    data sil existe /
  • noeud pointeur, debut, nouveaupoint,
    apres
  • pointeur debut trouve 0
  • while ((pointeur !NULL) (!trouve))
  • if pointeur-gtvaleur data
  • trouve 1
  • else pointeur pointeur-gtsuivant / aller
    à lélément suivant/
  • if (trouve) / élément existe /
  • nouveaupoint (noeud ) malloc(sizeof
    (noeud)) / alloue de la mémoire pour le nouvel
    élément/
  • nouveaupoint-gtvaleur valeur
  • apres pointeur-gtsuivant
  • nouveaupoint-gtsuivant apres
  • nouveaupoint-gtpreced pointeur
  • apres-gtpreced nouveaupoint
  • pointeur-gtsuivant nouveaupoint

80
Suppression dune valeur
  • void suppression (noeud debut, int data)
  • / supprimer lélément data sil existe /
  • noeud pointeur, debut, avant, apres
  • pointeur debut trouve 0
  • while ((nouveaupoint !NULL) (!trouve))
  • if pointeur-gtvaleur data
  • trouve 1
  • else pointeur pointeur-gtsuivant / aller
    à lélément suivant/
  • if (trouve) / élément existe /
  • / tester si cet élément est premier
    dans la liste /
  • avant pointeur-gtpreced
  • apres pointeur-gtsuivant
  • if (debut pointeur)
  • apres-gtpreced NULL
  • debut apres

81
Récursivité dans les listes chaînées
  • Reprenons les liste chainées et essayons de
    donner des versions récursives.

82
Création dune liste
  • include ltstdlib.hgt / donne accès à malloc /
  • typedef fin -1
  • noeud creation(void)
  • / construit la liste dentiers jusquà
    lecture de fin
  • int donnee
  • noeud ansp
  • / construit le premier élément, sil y en a
    un /
  • scanf(d,donnee)
  • if (datafin)
  • debut NULL
  • else debut (noeud ) malloc(sizeof
    (noeud))
  • debut-gt valeur donnee
  • debut -gtsuivant creation () /
    faire la même chose que précédemement/
  • return (debut) / retourne ladresse du
    premier élément /

83
Rechercher un élément
  • / cette fonction recherche un élément dans une
    liste /
  • noeud recherche (noeud debut, int data)
  • noeud pointeur
  • pointeur debut
  • if pointeur-gtvaleur data
  • return (pointeur)
  • else pointeur recherche(pointeur-gtsuivant,
    data)
  • return(NULL)

84
Les arbres
  • Définition Un arbre binaire est un ensemble de
    noeuds qui est, soit vide, soit composé dune
    racine et de deux arbres binaires disjoints,
    appelés sous-arbre droit et sous-arbre gauche.

1
2
5
85
  • Définition Un arbre binaire est dit de recherche
    (binary search tree) si, pour chaque noeud V,
    tous les éléments qui aont dans sous arbre gauche
    de V ont des valeurs inférieures à celle de V, et
    tous les éléments du sous-arbres droit de V sont
    supérieurs à celle de V.
  • Exemple

8
6
10
4
7
15
13
86
  • Remarqez que pour une suite de nombres, il existe
    différentes arbres binaires de recherche
    correspondant à cette liste. Tout dépend de la
    manièe dont sont présentés ces nombres.

87
  • La déclaration de cette structure de données est
    comme suit

Arbre binaire typedef struct listelement int
valeur struct listelement droit strcut
listelement gauche noeud noeud racine, p
88
Création de larbre
  • La fonction de création consiste à lire les
    données destinées à êre introduites dans les
    noeuds de larbre.
  • La création de larbre revient à insérer, à tour
    de rôle, des éléments dans larbre existant, en
    partant duin arbre vide.

89
EXEMPLE
  • SUPPOSONS QUE LA SUITE DE NOMBRES SUIVANTS EST
    INTRODUITE DANS CET ORDRE
  • 4 5 0 28
    45 7
  • LARBRE DE RECHERCHE OBTENU EST COMME SUIT

90
Version non récursive
  • noeud inserer(noeud racine, int data)
  • noeud p, q
  • if (racine NULL)/ arbre vide/
  • racine (noeud ) malloc(sizeof
    (noeud))
  • racine-gtvaleur data
  • racine-gtdroit NULLl
  • racine-gtgauche NULL
  • return(racine)
  • p racine
  • while (p ! NULL) / trouver lemplacement
    pour linsertion /
  • q p / sauvegarder le parent avant
    daller vers les enfants /
  • if (data lt p-gtvaleur)
  • p p-gtgauche / aller vers les
    enfants de gauche /
  • else p p -gtdroit / aller les enfants
    de droite /
  • p (noeud ) malloc(sizeof (noeud))
  • p -gtvaleur data
  • if (data lt q-gtvaleur)

91
Version récursive
  • noeud inserer(noeud racine, int data)
  • noeud p, q
  • if (racine NULL)
  • racine debut (noeud ) malloc(sizeof
    (noeud))/ allouer de lespace et mettre
    ladresse dans racine
  • racine-gtvaleur new
  • racine-gtgauche NULL
  • racine-gtdroit NULL
  • else
  • if data lt racine-gtvaleur
  • inserer(racine-gtgauche, data)
  • else inserer(racine-gtdroit, data)
  • return(racine)

92
  • typede fin -999
  • noeud creation(noeud racine)
  • racine NULL
  • for (scanf (d, donnee) donnee ! fin
    scanf (d, donnee))
  • inserer(racine, donnee)
  • return (racine) / retourne ladresse du
    premier élément /

93
Recherche dun élément dans un arbre binaire de
recherche
  • Parce que larbre possède la propriété dun arbre
    de recherche, la manière dy rechercher un
    élément est comem suit
  • 1. commencer à la racine
  • 2. si la valeur de ce noeud est lélément
    recherché, stop
  • 3. si la valeur du noeud courant est plus
    grande que la valeur recherchée, alors continuer
    la recherche dans la partie gauche de larbre.
  • 4. si la valeur du noeud courant est plus
    petite que la valeur recherchée, alors continuer
    la recherche dans la partie droite de larbre.
  • Cela nous consuit à lalgorithme suivant

94
  • noeud rechercher (noeud racine, int data)
  • noeud p int trouve
  • p racine trouve 0
  • while (( p ! NULL) (!trouve))
  • if (p-gtvaleur data)
  • trouve 1
  • else if (p-gtgauche gt data)
  • p p-gtgauche
  • else p p-gtdroit
  • if (trouve)
  • printf(élément trouvé)
  • else printf(élément inexistant)
  • return(p)

95
La version récursive est comme suit
  • noeud rechercher (noeud racine, int data)
  • if (racine NULL)
  • p NULL
  • else if (racine-gtvaleur data)
  • p racine
  • else if (racine-gtvaleur gt data)
  • p recherche(racine-gtgauc
    he,data)
  • else p recherche(racine-gtdr
    oit,data)
  • if (p NULL)
  • printf(élément inexistant )
  • else printf(élément existe)
  • return(p)

96
Suppression dun élément dans un arbre binaire de
recherche
  • Cette opération doit être telle que larbre qui
    va en résulter doit toujours rester un arbre
    binaire de recherche.
  • Cette opération est un peu plus complexe et plus
    longue que celle de linsertion bien quelle ne
    consiste quen quelques miouvements de pointeurs
  • On peut penser que la suppression dun élément
    consiste à le trouver, puis à relier la structure
    sous-jacente à celle qui subsiste.
  • La structure de la fonction supprimer semble
    similaire à celle de la fonction rechercher.
  • Elle lest effectivement, mais dans le cas où
    lélément à supprimer est trouvé, il convient de
    dsitinguer plusiers cas de figures
  • CAS 1 on enlve un élément situé à la racine
    illustré par la figure suivante

Élément à supprimer
B
C
A
97
  • Daprès la fonction de création, tous les
    éléments de C sont plus grands que ceux de A.
  • Premier cas particulier celui où C est vide.
    Dans ce cas, A devient la racine.
  • Dans la cas contraire, on al choix entre A et C,
    pour la racine.
  • Choisit-on A? Lélément le plus grand de A est
    alors inférieur à lélément le plus petit de B.
  • Il faut donc rechercher lélément le plus grand
    de A, en parcourant ses branches de droite
    jusquà ce quon atteigne une feuille de larbre,
    puis remplacer le pointeur de droite de cet
    éléemnt par lancien pointeur droit, qui
    pointait aupravent vers B.

98
  • racine ancien pointeur gauche
    de B

A
Ancien pointeur droit de B
C
99
Avec C comme racine, nous aurions
  • racine ancien pointeur droit
    de B

C
Ancien pointeur gacuhe de B
A
100
  • CAS 2 Cest le cas où lélément enlevé nest pas
    la racine de larbre.dans ce cas, il faut alors
    anticiper la recherche de cet élément pour
    pouvoir raccrocher les pointeurs de la structure
    sous-jacente à ceux de la structure sus-jacente à
    lélément concerné.
  • Soit par exemple, la suppression de B dans la
    structure suivante

101
D
B
Élément à supprimer
A
C
On a A lt B lt C lt D
Si on supprime B, on a donc A
lt C lt D
102
  • Ceci implique, soit une structure du type
    suivant, qui réalise un chagement de racine et
    une rotation de toute la structure à droite

C
A
D
103
  • Soit une structure de cet autre type, où la
    structure sus-jacente nest pas modifiée

D
C
A
104
  • Dans le cas où C, on a simplement

D
A
105
  • Nous retiendrons, et programmerons, la deuxième
    solution.
  • Lancien pointeur droit de B devient le pointeur
    gauche de D. Tandis que le pointeur de lélément
    le plus à gauche de C est maintenant égal à
    lancien pointeur gauche de B.
  • Le cas où C est vide est traité par le simple
    remplacement de lancien pointeur gauche de D
    par lancien pointeur gauche de B.

106
  • CAS 3.
  • Ce cas est symétrique au précédent. Soit une
    structure du type

D
Élément à supprimer
F
E
G
107
  • Dans ce cas, nous avons
  • D lt E lt F lt G
  • Si on supprime F, on a
  • D lt E lt
    G
  • Ce qui donne deux possibilités

108
E
G
D
Ou bien
D
E
G
109
  • Le raisonnement est tout à fait similaire, et
    symétrique, à celui qui a été vu à gauche.
  • Nous obtenons alors la fonction récursive
    suivante

110
  • noeud supprimer(noeud racine, int data)
  • noeud PG, PT, POINTD
  • if (racine NULL)
  • printf(mot inexistant)
  • else
  • if (racine-gtvaleur data)/ supprimer la
    racine/
  • PT racine-gtdroite
  • if (PT ! NULL)
  • while(PT ! NULL)
  • PG PT
  • PT PT-gtgauche / aller
    chercher le plus petitélément de la partie droite
    /
  • PG-gtgauche racine-gtgauche
  • racine racine-gtdroite
  • / fin du if
  • else
  • racine racine -gtgauche
  • else /de racine-gt ! data /

111
  • if (data racine-gtgauche-gtvaleur)
  • printf(élément trouvé à gauche)
  • PT racine-gtgauche-gtdroite
  • if (PT ! NULL)
  • while (PT! NULL)
  • PG PT
  • PT PT-gtgauche
  • PG-gtgauche racine-gtgauche-gtgauche
  • racine-gtgauche racine-gtgauche-gtdroite
  • /fin du if /
  • else racine -gtgauche racine
    -gtgauche-gtgauche
  • else / data ! racine-gtgauche-gtvaleur /

112
  • If (data racine -gtdroite-gtvaleur)
  • printf(élément trouvé à droite)
  • PT racine-gtdroite-gtgauche
  • If (PT ! NULL)
  • while (PT ! NULL)
  • POINTD PT
  • PT PT-gtdroite
  • POINTD-gtdroite racine-gtdroite-gtdroite
  • racine-gtdroite racine -gtdroite-gtgauche
  • else racine -gtdroite racine -gtdroite-gtdroite
  • else / data ! racine-gtdroite-gtvaleur /

113
  • if (data lt racine-gtvaleur)
  • supprimer(racine-gtgauche,data)
  • else
  • supprimer(racine-gtdroite,data)

114
Parcours dans un arbre
  • Il existe différentes manière de parcourir un
    arbre
  • Parcours postfix
  • Parcours infix
  • Parcours prefix

115
Exemple
  • Dans la parcours postfix, pour un noeud donné, il
    est visité aprés les noeuds de sa partie gauche
    et de sa partie droite.
  • Dans le parcours infix la partie gauche dun
    noeud est visité, ensuite le noeud lui-même, et
    enfin sa partie droite.

116
  • Dans le parcours, le noeud en question est
    visité, ensuite sa partie gauche et enfin sa
    partie droite.

117
Exemple
21
12
23
8
15
27
20
6
9
14
118
  • Le parcours en postfixe
  • 6 9 8 14 15 12 20 27 23 21
  • Le parcours en infixe
  • 6 8 9 12 14 15 20 21 23 27
  • Le parcours en prefixe
  • 21 12 8 6 9 15 14 23 20 27

119
  • void postfixe(noeud racine)
  • if (racine ! NULL)
  • postfixe(racine-gtgauche)
  • postfixe(racine -gtdroite)
  • printf(d,racine-gtvaleur)
  • void infixe(noeud racine)
  • if (racine ! NULL)
  • infixe(racine-gtgauche)
  • printf(d,racine-gtvaleur)
  • postfixe(racine -gtdroite)

120
  • void prefixe(noeud racine)
  • if (racine ! NULL)
  • printf(d,racine-gtvaleur)
  • prefixe(racine-gtgauche)
  • prefixe(racine -gtdroite)

121
Les arbres (suite)
  • Soit larbre suivant

N
........
Tk
T2
T1
T3
122
  • Le parcours en prefixe des noeuds de cet arbre
    est la racine n suivie des neouds de T1 en
    prefixe, puis ceux de T2, T3, ... Et Tk en
    prefixe
  • Le parcours en infixe des noeuds de cet arbre est
    lesnoeuds de T1 en infixe, suivi de la racine n,
    suivies des noeuds de T2, T3, ... et Tk en
    infixe
  • Le parcours en postfixe des noeuds de cet arbre
    est les noeuds de T1, puis de T2, T3, ... et Tk
    en postfixe, ensuite la racine n

123
  • Le parcours en postfixe se fait comme suit
  • Prefixe(V)
  • Pour chaque enfant C de V, de gauche à droite,
    faire prefixe(C)
  • Visiter C

124
  • Le parcours en infixe se fait comme suit
  • Infixe(V)
  • if V est une feuille
  • visiter V
  • else
  • infixe(enfent gauche de V)
  • visiter V
  • pour chaque enfant C de V, de gauche à
  • droire, excepté le plus à gauche,
  • faire infixe(C)

125
  • Le parcours en postfixe est comme suit
  • Pour chaque enfant C de V, de gauche à droite,
    faire postfixe(C)
  • Visiter C

126
Exemple
1
2
4
3
5
6
7
8
9
12
10
11
13
14
127
  • En prefixe, le parcours de cet arbre est comme
    suit
  • En postfixe, le parcours de cet arbre est comme
    suit
  • En infixe, le parcours de cet arbre est comme
    suit

128
Implantation des arbres dans le cas général
  • Comme le nombre de descendants varie dun noeud à
    un autre, la déclaration dun noeud se fera
    comme suit
  • Chaque noeud comprendra
  • 1. la valeur de ce noeud
  • 2. un pointeur vers le descendant le plus à
    gauche
  • 3. un pointeur vers le frère/soeur (de même
    niveau) de droite.

129
  • typedef struct listelement
  • int valeur
  • struct listelement freredroit
  • strcut listelement enfantgauche
  • noeud
  • noeud racine, p

130
  • Si par exemple, les fonctions suivantes
    existaient déjà
  • noeud enfant_gauche(noeud n) retournant
    ladresse du descendant le plus à gauche de n
  • noeud frere_droit(noeud n) retournant ladresse
    du frère droit de n, alors la fonction prefixe
    peut être comme suit

131
  • void prefixe(noeud racine)
  • noeud C
  • prinf(d,racine-gtvaleur)
  • C enfant_gauche(racine)
  • while (C ! NULL)
  • prefixe(C)
  • C frere_droit(C)
  • / fin de la fonction /
Write a Comment
User Comments (0)
About PowerShow.com