You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/TDs/S2/06_binaryTree.md
+22-24Lines changed: 22 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ Dans ce TD, nous allons voir comment implémenter un arbre binaire de recherche.
8
8
9
9
## Exercice 1 (Implémentation)
10
10
11
-
Donnons nous pour commencer la structure suivante pour représenter un nœud d'un arbre binaire.
11
+
Donnons nous pour commencer la structure suivante pour représenter un nœud d'un arbre binaire :
12
12
13
13
```cpp
14
14
structNode {
@@ -27,7 +27,7 @@ src/
27
27
L main.cpp
28
28
L node.hpp
29
29
L node.cpp
30
-
CmakeLists.txt
30
+
CMakeLists.txt
31
31
```
32
32
:::
33
33
@@ -72,7 +72,7 @@ void Node::insert(int value);
72
72
On utilisera la fonction `create_node` pour créer le nouveau nœud.
73
73
:::
74
74
75
-
4. Écrire une méthode `height` qui retourne la hauteur de l'arbre binaire (c'est à dire la longueur du plus long chemin entre la racine et une feuille).
75
+
4. Écrire une méthode `height` qui retourne la hauteur de l'arbre binaire (c'est à dire la longueur du plus long chemin entre la racine et une feuille) (On va se donner la convention qu'un arbre binaire contenant un seul nœud a une hauteur de 1)
76
76
```cpp
77
77
int Node::height() const;
78
78
```
@@ -82,44 +82,42 @@ On peut utiliser de la récursivité pour calculer la hauteur de l'arbre.
82
82
:::
83
83
84
84
85
-
5. Écrire une fonction `delete_childs` sur la structure `Node` qui permet de supprimer les fils d'un nœud (et de libérer la mémoire).
85
+
5. Écrire une fonction `delete_children` sur la structure `Node` qui permet de supprimer les fils d'un nœud (et de libérer la mémoire).
86
86
```cpp
87
-
voidNode::delete_childs();
87
+
voidNode::delete_children();
88
88
```
89
89
90
90
:::tip
91
91
On peut utiliser de la récursivité pour supprimer les nœuds de l'arbre.
92
92
:::
93
93
94
-
On va se donner la convention qu'un arbre binaire contenant un seul nœud a une hauteur de 1.
95
-
96
-
6. Écrire une **méthode**`display_infixe` qui affiche les valeurs des nœuds parcourus dans l'ordre [**infixe**](/Lessons/S2/BinaryTree/#parcours-en-profondeur).
94
+
6. Écrire une **méthode**`display_infix` qui affiche les valeurs des nœuds parcourus dans l'ordre [**infixe**](/Lessons/S2/BinaryTree/#parcours-en-profondeur).
97
95
```cpp
98
-
voidNode::display_infixe() const;
96
+
voidNode::display_infix() const;
99
97
```
100
98
101
-
7. Écrire une **méthode**`prefixe` qui retourne un vecteur contenant des pointeurs vers les nœuds de l'arbre binaire parcourus en [**prefixe**](/Lessons/S2/BinaryTree/#parcours-en-profondeur).
99
+
7. Écrire une **méthode**`prefix` qui retourne un vecteur contenant des pointeurs vers les nœuds de l'arbre binaire parcourus en [**prefixe**](/Lessons/S2/BinaryTree/#parcours-en-profondeur).
102
100
```cpp
103
-
std::vector<Node const*> Node::prefixe() const;
101
+
std::vector<Node const*> Node::prefix() const;
104
102
```
105
103
106
104
:::tip
107
105
Pour le faire par **récursivité** on pourra utiliser la méthode `insert` du `std::vector` qui permet d’insérer plusieurs éléments à l'aide d’itérateurs.
Cela va permettre de concaténer dans un seul vecteur les nœuds des sous arbres gauche et droit.
113
111
:::
114
112
115
-
8. (**BONUS**) De même, écrire une autre méthode `postfixe` qui retournent les nœuds parcourus dans l'ordre [**postfixe**](/Lessons/S2/BinaryTree/#parcours-en-profondeur).
113
+
8. (**BONUS**) De même, écrire une autre méthode `postfix` qui retournent les nœuds parcourus dans l'ordre [**postfixe**](/Lessons/S2/BinaryTree/#parcours-en-profondeur).
116
114
117
115
<details>
118
116
<summary>BONUS: Itératif</summary>
119
117
120
-
si tu le souhaites, tu peux essayer de le faire de manière itérative (sans récursivité).
118
+
Si tu le souhaites, tu peux essayer de le faire de manière itérative (sans récursivité).
121
119
122
-
Pour faire cela tu peux utiliser une pile (`std::stack`) pour stocker les nœuds à parcourir. L'idée est de parcourir l'arbre en commençant par la racine (premier élément de la pile). Puis, de déplier un nœud de la pile, s'il a un fils droit, on le met dans la pile et on recommence. Sinon, s'il a un fils gauche, on le met dans la pile et on recommence. enfin s'il n'a pas de fils, on le traite (on peut l'ajouter à un vecteur par exemple). Il faut aussi faire attention à conserver un pointeur vers le nœud précédent afin de savoir si on remonte ou si on descend dans l'arbre.
120
+
Pour faire cela tu peux utiliser une pile (`std::stack`) pour stocker les nœuds à parcourir. L'idée est de parcourir l'arbre en commençant par la racine (premier élément de la pile). Puis, de dépiler un nœud de la pile, s'il a un fils droit, on le met dans la pile et on recommence. Sinon, s'il a un fils gauche, on le met dans la pile et on recommence. Enfin s'il n'a pas de fils, on le traite (on peut l'ajouter à un vecteur par exemple). Il faut aussi faire attention à conserver un pointeur vers le nœud précédent afin de savoir si on remonte ou si on descend dans l'arbre.
123
121
124
122
Exemple:
125
123
Si on a l'arbre suivant:
@@ -143,10 +141,10 @@ C'est un peu plus compliqué que la version récursive mais c'est un bon exercic
143
141
Voilà un bout de code pour vous aider à démarrer:
144
142
```cpp
145
143
146
-
std::vector<Node const*> Node::postfixe() const {
144
+
std::vector<Node const*> Node::postfix() const {
147
145
std::vector<Node const*> nodes {};
148
146
std::stack<Node const*> to_process {};
149
-
Node* previous {nullptr};
147
+
Node const* previous {nullptr};
150
148
to_process.push(this);
151
149
152
150
while (!to_process.empty()) {
@@ -216,15 +214,15 @@ Il existe trois cas de figure lorsqu'on supprime un nœud d'un arbre binaire:
216
214
- Le nœud n'a pas de fils: on peut le supprimer directement (exemple précédent).
217
215
- Le nœud a un seul fils: on peut le supprimer et le remplacer par son fils.
218
216
- Le nœud a deux fils:
219
-
Il faut remplacer la valeur du nœud à supprimer par une valeur préserver l'ordre de l'arbre. Pour cela, on utilisera la fonction `most_left` qui permettent de trouver le nœud le plus à gauche d'un arbre binaire (autrement dit, le nœud de valeur minimale).
217
+
Il faut remplacer la valeur du nœud à supprimer par une valeur pour préserver l'ordre de l'arbre. Pour cela, on utilisera la fonction `most_left` qui permettent de trouver le nœud le plus à gauche d'un arbre binaire (autrement dit, le nœud de valeur minimale).
220
218
Il faut ensuite remplacer la valeur du nœud à supprimer par la valeur du nœud trouvé précédemment et supprimer ce dit nœud pour ne pas avoir de doublon. (cela revient à intervertir les deux nœuds sans avoir à modifier les pointeurs puis à supprimer le nœud dont la valeur a été copiée).
221
219
222
220
Ce troisième cas est le plus compliqué à gérer. N'hésitez pas à demander de l'aide et prendre le temps de faire des schémas pour comprendre le fonctionnement.
223
221
Il faudra utiliser la fonction `most_left` pour trouver le nœud le plus à gauche de l'arbre binaire.
224
222
:::
225
223
226
224
:::warning
227
-
Il faut faire attention à bien libérer la mémoire des nœuds supprimés.
225
+
Il faut faire attention à bien libérer la mémoire des nœuds supprimés, avec `delete`.
228
226
:::
229
227
230
228
11. Écrire une **fonction** `delete_tree` qui permet de supprimer un arbre binaire (et de libérer la mémoire).
@@ -244,7 +242,7 @@ Testons maintenant notre implémentation en créant un programme qui permet de c
244
242
245
243
3. (**BONUS**) Afficher la valeur minimale et maximale de l'arbre.
246
244
247
-
4. Afficher la somme des valeurs des nœuds de l'arbre binaire en utilisant la fonction `prefixe` qui retourne un vecteur contenant les nœuds parcourus dans l'ordre **prefixe**.
245
+
4. Afficher la somme des valeurs des nœuds de l'arbre binaire en utilisant la fonction `prefix` qui retourne un vecteur contenant les nœuds parcourus dans l'ordre **prefixe**.
248
246
249
247
5. Afficher la hauteur de l'arbre binaire.
250
248
@@ -253,7 +251,7 @@ Testons maintenant notre implémentation en créant un programme qui permet de c
253
251
254
252
Nous allons maintenant améliorer et simplifier notre code en utilisant des [**pointeurs intelligents**](/Lessons/S1/MemoryAllocation). En effet la partie la plus compliquée de notre code est la gestion de la mémoire et des pointeurs. Les pointeurs intelligents vont nous permettre de nous débarrasser de cette gestion et de nous passer de la fonction `delete_tree` par exemple.
255
253
256
-
1. Copiercoller votre fichier pour garder une version de votre code précédent et créer un nouveau fichier `smartNode.hpp` dans lequel vous allez réécrire votre code en utilisant des **pointeurs intelligents**.
254
+
1. Copier-coller votre fichier pour garder une version de votre code précédent et créer un nouveau fichier `smartNode.hpp` dans lequel vous allez réécrire votre code en utilisant des **pointeurs intelligents**.
257
255
Renommer la structure `Node` en `SmartNode` pour pouvoir faire la différence entre les deux versions et remplacer les pointeurs "bruts" par des **pointeurs intelligents**`std::unique_ptr` dans la structure `SmartNode`.
258
256
259
257
Je vous donne le contenu du fichier `smartNode.hpp`:
5. Modifier la méthode `remove` pour qu'elle utilise des pointeurs intelligents.
307
305
308
306
:::tip
309
-
C'est un peu plus compliqué car il faut utiliser des références vers des pointeurs intelligents pour pouvoir les modifier. On peut utiliser les méthodes `reset` et `release` pour gérer la mémoire et les pointeurs intelligents. Ou utiliser un concept plus avancé `std::move` pour transférer la propriété d'un pointeur intelligent d'un objet à un autre.
307
+
C'est un peu plus compliqué car il faut utiliser des références vers des pointeurs intelligents pour pouvoir les modifier. On peut utiliser les méthodes `reset` et `release` (allez lire la documentation pour voir la différence entre les deux !) pour gérer la mémoire et les pointeurs intelligents. Ou utiliser un concept plus avancé `std::move` pour transférer la propriété d'un pointeur intelligent d'un objet à un autre.
310
308
Si vous voulez essayer, n'hésitez pas à demander de l'aide.
311
309
:::
312
310
313
311
### Exercice 4 (Encapsulation) (Bonus)
314
312
315
313
Le but est de créer une structure `BinaryTree` qui encapsule la structure `Node` ou `SmartNode` et qui permet d'utiliser les même méthodes sans connaître la structure interne de l'arbre binaire. Cela permet aussi de gérer le cas où l'arbre binaire est vide (c'est à dire que la racine est un pointeur nul).
316
314
317
-
C'est moins pertinent dans notre cas pour ce TDs mais lorsque vous découvrirez la notion de **visibilité** vous comprendrez l'intérêt de cette encapsulation.
315
+
C'est moins pertinent dans notre cas pour ce TD mais lorsque vous découvrirez la notion de **visibilité** (public / privé) vous comprendrez l'intérêt de cette encapsulation.
0 commit comments