Skip to content

Commit dfe8e8a

Browse files
authored
Merge pull request #40 from JulesFouchy/S2_TD6
[S2 TD6] Tweaks
2 parents 3ad47df + 4257df2 commit dfe8e8a

File tree

1 file changed

+22
-24
lines changed

1 file changed

+22
-24
lines changed

content/TDs/S2/06_binaryTree.md

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Dans ce TD, nous allons voir comment implémenter un arbre binaire de recherche.
88

99
## Exercice 1 (Implémentation)
1010

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 :
1212

1313
```cpp
1414
struct Node {
@@ -27,7 +27,7 @@ src/
2727
L main.cpp
2828
L node.hpp
2929
L node.cpp
30-
CmakeLists.txt
30+
CMakeLists.txt
3131
```
3232
:::
3333
@@ -72,7 +72,7 @@ void Node::insert(int value);
7272
On utilisera la fonction `create_node` pour créer le nouveau nœud.
7373
:::
7474
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)
7676
```cpp
7777
int Node::height() const;
7878
```
@@ -82,44 +82,42 @@ On peut utiliser de la récursivité pour calculer la hauteur de l'arbre.
8282
:::
8383

8484

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).
8686
```cpp
87-
void Node::delete_childs();
87+
void Node::delete_children();
8888
```
8989

9090
:::tip
9191
On peut utiliser de la récursivité pour supprimer les nœuds de l'arbre.
9292
:::
9393

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).
9795
```cpp
98-
void Node::display_infixe() const;
96+
void Node::display_infix() const;
9997
```
10098

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).
102100
```cpp
103-
std::vector<Node const*> Node::prefixe() const;
101+
std::vector<Node const*> Node::prefix() const;
104102
```
105103

106104
:::tip
107105
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.
108106
```cpp
109-
auto left_nodes {left->infixe()};
107+
auto left_nodes {left->prefix()};
110108
nodes.insert(nodes.end(), left_nodes.begin(), left_nodes.end());
111109
```
112110
Cela va permettre de concaténer dans un seul vecteur les nœuds des sous arbres gauche et droit.
113111
:::
114112
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).
116114
117115
<details>
118116
<summary>BONUS: Itératif</summary>
119117
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é).
121119
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.
123121
124122
Exemple:
125123
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
143141
Voilà un bout de code pour vous aider à démarrer:
144142
```cpp
145143
146-
std::vector<Node const*> Node::postfixe() const {
144+
std::vector<Node const*> Node::postfix() const {
147145
std::vector<Node const*> nodes {};
148146
std::stack<Node const*> to_process {};
149-
Node* previous {nullptr};
147+
Node const* previous {nullptr};
150148
to_process.push(this);
151149
152150
while (!to_process.empty()) {
@@ -216,15 +214,15 @@ Il existe trois cas de figure lorsqu'on supprime un nœud d'un arbre binaire:
216214
- Le nœud n'a pas de fils: on peut le supprimer directement (exemple précédent).
217215
- Le nœud a un seul fils: on peut le supprimer et le remplacer par son fils.
218216
- 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).
220218
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).
221219
222220
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.
223221
Il faudra utiliser la fonction `most_left` pour trouver le nœud le plus à gauche de l'arbre binaire.
224222
:::
225223
226224
:::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`.
228226
:::
229227
230228
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
244242

245243
3. (**BONUS**) Afficher la valeur minimale et maximale de l'arbre.
246244

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**.
248246

249247
5. Afficher la hauteur de l'arbre binaire.
250248

@@ -253,7 +251,7 @@ Testons maintenant notre implémentation en créant un programme qui permet de c
253251

254252
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.
255253

256-
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**.
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**.
257255
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`.
258256

259257
Je vous donne le contenu du fichier `smartNode.hpp`:
@@ -296,7 +294,7 @@ if (ptr) {
296294

297295
Elle s'utilise de la manière suivante:
298296
```cpp
299-
std::unique_ptr<float> ptr {std::make_unique<float>(3.14)};
297+
std::unique_ptr<float> ptr {std::make_unique<float>(3.14f)};
300298
```
301299
302300
3. Modifier la méthode `most_left` pour qu'elle retourne une référence vers un pointeur intelligent au lieu d'un pointeur brut.
@@ -306,15 +304,15 @@ std::unique_ptr<float> ptr {std::make_unique<float>(3.14)};
306304
5. Modifier la méthode `remove` pour qu'elle utilise des pointeurs intelligents.
307305
308306
:::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.
310308
Si vous voulez essayer, n'hésitez pas à demander de l'aide.
311309
:::
312310
313311
### Exercice 4 (Encapsulation) (Bonus)
314312
315313
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).
316314
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.
318316
319317
Voilà le contenu du fichier `binaryTree.hpp`:
320318

0 commit comments

Comments
 (0)